Serving ownCloud with Caddy

In this post, I’ll walk you through how to set up ownCloud with Caddy for a secure, personal cloud service. I wrote this guide while configuring on Ubuntu 14.

ownCloud

A quick introduction to ownCloud for those who never heard about it (as found on Wikipedia):

OwnCloud (stylized ownCloud) is a suite of client-server software for creating file hosting services and using them. OwnCloud is functionally very similar to the widely used Dropbox, with the primary functional difference being that OwnCloud is free and open-source, and thereby allowing anyone to install and operate it without charge on a private server, with no limits on storage space (except for disk capacity or account quota) or the number of connected clients.

Installing MariaDB

ownCloud requires a database server, so we’ll start by installing MariaDB.
The following command will install MariaDB server and client:

$ sudo apt-get install mariadb-server

Once you have MariaDB installed you need to secure your installation. This will guide you through some steps.

$ sudo /usr/bin/mysql_secure_installation

Creating the ownCloud user and database

Now you need to login to the MySQL command line with the root credentials.
Use the following command, and type your root password.

$ mysql -u root -p

Once logged in we create a new database for ownCloud:

MariaDB [(none)]> create database owncloud;

and a new user:

MariaDB [(none)]> grant usage on *.* to owncloud@localhost identified by 'somepassword';

and give that user access to the newly created database:

MariaDB [(none)]> grant all privileges on owncloud.* to owncloud@localhost;

Now you have a user owncloud with password somepassword which has access to the database called owncloud.

Installing PHP-FPM

Since PHP 7 was released a couple of months ago, there is no reason not to install this newer version.

First we need to add the repository which contains PHP 7:

$ sudo add-apt-repository ppa:ondrej/php
$ sudo apt-get update

Now we can install PHP:

$ sudo apt-get install php7.0-fpm

Simply installing PHP isn’t enough, in order to have ownCloud working properly, you also need some extra PHP extensions. A list of all ownCloud requirements can be found here. The extensions below are recommended for ownCloud and not included in the default PHP installation:

  • PHP mysql driver
  • PHP gd
  • PHP curl
  • PHP intl
  • PHP mcrypt
  • PHP imagick

You can install them all at once:

$ sudo apt-get install php7.0-mysql php7.0-gd php7.0-curl php7.0-intl php7.0-mcrypt 

If you need previews for videos and documents you also need to install the following (non-PHP) packages:

  • ffmpeg or avconv
  • LibreOffice

For optimal integration with Caddy, accepting PHP-FPM request on a TCP socket instead of a Unix socket is better:
Replace listen = /run/php/php7.0-fpm.soc to listen = 127.0.0.1:9000 in /etc/php/7.0/fpm/pool.d/www.conf.

Don’t forget to restart PHP-FPM: sudo service php7.0-fpm restart after installing all extensions.

Caddyfile

I made the following Caddyfile together with mholt and dprandzioch. The config contains everything you need for hosting ownCloud server and also supports the desktop and mobile clients.

my-owncloud-site.com {

    root owncloud
    log owncloud/access.log
    errors owncloud/access.log

    fastcgi / 127.0.0.1:9000 php {
            env PATH /bin
    }

    rewrite {
        r ^/index.php/.*$
        to /index.php?{query}
    }

    # client support (e.g. os x calendar / contacts)
    redir /.well-known/carddav /remote.php/carddav 301
    redir /.well-known/caldav /remote.php/caldav 301

    # remove trailing / as it causes errors with php-fpm
    rewrite {
        r ^/remote.php/(webdav|caldav|carddav)(\/?)$
        to /remote.php/{1}
    }

    rewrite {
        r ^/remote.php/(webdav|caldav|carddav)/(.+)(\/?)$
        to /remote.php/{1}/{2}
    }

    status 403 /forbidden

    # .htacces / data / config / ... shouldn't be accessible from outside
    rewrite {
        r ^/(?:\.htaccess|data|config|db_structure\.xml|README)
        to /forbidden
    }
    
    header / Strict-Transport-Security "15768000"
    
}

Thanks to Caddy’s Let’s Encrypt integration, our ownCloud installation is automatically secured and served over HTTPS. Access and error logs are written to the ownCloud folder, and the data folder (together with other special files) is protected against requests from the outside.

If you want to test your Caddyfile / PHP installation, you can create a phpinfo.php file in the owncloud directory, and put the following line into it:

<?php phpinfo(); ?>

Then navigate to https://my-owncloud-site.com/phpinfo.php with your browser and check if the default PHP info page is displayed. (of course after Caddy is running with your Caddyfile)
Don’t forget to delete this file after everything is working! Other people don’t need to know your exact PHP installation with limits and extensions.

Installing & Configuring ownCloud

Now it’s finally time to install ownCloud.

Download the latest ownCloud version (at the time of writing 9.0.0 was the latest version, check this for yourself):

$ wget https://download.owncloud.org/community/owncloud-9.0.0.zip

Unzip the files into the owncloud directory:

$ unzip owncloud-9.0.0.zip

Go to https://my-owncloud-site.com with your web browser. If everything works fine, you will see the ownCloud configuration screen.

However, you need to create a data folder for ownCloud and give ownCloud (i.e. www-data user) write/read access to it.

$ mkdir owncloud/data
$ sudo chown -R www-data owncloud

Concluding

Caddy’s rapid development makes it a choice candidate for serving your OwnCloud installation securely and easily.

This blogpost was originally written for the official Caddy blog: Running ownCloud with Caddy

Join the Conversation

33 Comments

  1. I’m trying this out from a Ubuntu 14.04 docker container. I’m having a problem with the php part.
    I’ve changed the listen conf as described below:
    https://github.com/caddyserver/caddyserver.com/pull/37#discussion-diff-56527424

    I’ve changed the first line of the Caddyfile to :2020 { so I could test it locally.

    I’ve created the phpinfo.php in the owncloud directory. But I’m getting File not found.
    when I visit http://172.17.0.2:8080/phpinfo.php. (172.17.0.2 is the ip address of the docker container).

    When I use wget inside the docker container to test it gives the same error:

    # wget “localhost:8080/phpinfo.php”
    –2016-03-17 16:28:07– http://localhost:8080/phpinfo.php
    Resolving localhost (localhost)… ::1, 127.0.0.1
    Connecting to localhost (localhost)|::1|:8080… connected.
    HTTP request sent, awaiting response… 404 Not Found
    2016-03-17 16:28:07 ERROR 404: Not Found.

    When I comment out the fastcgi config in the Caddyfile I get the php contained in the file:

    Running # service php7.0-fpm status tells me php is running.

    owncloud/access.log contains:
    172.17.0.1 – [17/Mar/2016:16:18:57 +0000] “GET /phpinfo.php HTTP/1.1” 404 16
    17/Mar/2016:16:28:07 +0000 [ERROR 404 /phpinfo.php] Primary script unknown

    Am I missing a step? I’m hoping this isn’t a docker specific thing.

      1. In the same one.

        I’ve now also tried having caddy connecting to that default socket by using the following config in the Caddyfile:

        fastcgi / /run/php/php7.0-fpm.sock php

        This gives the same result.

        Why is the env PATH /usr/bin config needed? Could it be that it doesn’t find the php executable because it’s installed in /usr/bin/? Adding /usr/bin instead of /bin doesn’t help.

        1. Primary script unknown
          This means that Caddy passes a script to PHP-FPM, but PHP can’t find the file, this happens e.g. when you run PHP-FPM in Docker, but Caddy on your guest OS. You need to make sure that the correct volumes are attached to the Docker container.

        2. Why is the env PATH /usr/bin config needed? Could it be that it doesn’t find the php executable because it’s installed in /usr/bin/? Adding /usr/bin instead of /bin doesn’t help.

          This is needed so PHP (i.e. ownCloud) can use binaries on the system. This has nothing to do with our current problem…

      2. # ls -l
        total 36
        -rw-r–r– 1 root root 122 Mar 17 17:17 Caddyfile
        -rw-r–r– 1 root root 20433 Mar 17 17:21 access.log
        -rw-r–r– 1 root root 7 Mar 17 16:39 index.html
        -rwxr-xr-x 1 www-data www-data 20 Mar 17 16:37 phpinfo.php

        # ps aux
        USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
        root 1 0.0 0.0 18316 3524 ? Ss 13:33 0:00 /bin/bash
        root 8081 0.0 0.0 18152 3048 ? S 14:02 0:00 /bin/bash /usr/bin/mysqld_safe
        root 8082 0.0 0.0 4344 1544 ? S 14:02 0:00 logger -p daemon.err -t /etc/init.d/mysql -i
        mysql 8432 0.1 1.3 583088 106724 ? Sl 14:02 0:13 /usr/sbin/mysqld –basedir=/usr –datadir=/var/lib/mysql –plugin-dir=/usr/lib/mysql/plu
        root 8569 0.0 0.1 13884 9788 ? Tl 14:13 0:00 caddy
        root 9851 0.0 0.1 332628 13496 ? Ss 17:17 0:00 php-fpm: master process (/etc/php/7.0/fpm/php-fpm.conf)
        www-data 9852 0.0 0.1 332944 11556 ? S 17:17 0:00 php-fpm: pool www
        www-data 9853 0.0 0.1 332944 11556 ? S 17:17 0:00 php-fpm: pool www
        root 9906 0.0 0.0 15572 2072 ? R+ 17:27 0:00 ps aux

        1. Caddy runs as user root, but PHP-FPM as user www-data. Does PHP has access to the files Caddy is passing to it?

          See my other comment:

          Primary script unknown
          This means that Caddy passes a script to PHP-FPM, but PHP can’t find the file, this happens e.g. when you run PHP-FPM in Docker, but Caddy on your guest OS. You need to make sure that the correct volumes are attached to the Docker container.
          </blockquote

          1. Thanks for all the help, I’m pretty sure this has nothing to anymore with your caddy owncloud script, but maybe in the end I can help with some comment that can prevent server noobs like me getting into this issue.

            I’m doing all this in one container, it’s just for the experiment. I find this easier than starting a virtual machine and I don’t want to clutter my main system.

            I’m trying to visit phpinfo.php, because I was concerned with rights I did put that file under www-data:www-data.
            Is there another log I can check? Can’t I confirm somewhere that it can find the file, but that it doesn’t have the right to read it for example?
            I did check the php log, even after upping the log level to debug, but it mostly contains the same error.

          2. I did chmod the file into 777, but that doesn’t help.

            Not sure how to run php as root, if I try that by changing the user and group in /etc/php/7.0/fpm/php-fpm.conf, but then it doesn’t start and `/var/log/php7.0-fpm.log` contains:
            ERROR: pid 677, fpm_unix_conf_wp(), line 300: [pool www] please specify user and group other than root.

            Do you know of another log I could check?

          3. /var/log/php7.0-fpm.log contains:

            [17-Mar-2016 22:33:06.344469] DEBUG: pid 448, fpm_log_open(), line 50: open access log (/usr/log/www.access.log)
            [17-Mar-2016 22:33:06.344883] DEBUG: pid 448, fpm_unix_init_main(), line 520: The calling process is waiting for the master process to ping via fd=6
            [17-Mar-2016 22:33:06.345790] DEBUG: pid 449, fpm_scoreboard_init_main(), line 40: got clock tick ‘100’
            [17-Mar-2016 22:33:06.346247] DEBUG: pid 449, fpm_socket_af_inet_listening_socket(), line 298: Found address for 127.0.0.1, socket opened on 127.0.0.1
            [17-Mar-2016 22:33:06.346271] DEBUG: pid 449, fpm_event_init_main(), line 342: event module is epoll and 11 fds have been reserved
            [17-Mar-2016 22:33:06.346344] NOTICE: pid 449, fpm_init(), line 85: fpm is running, pid 449
            [17-Mar-2016 22:33:06.346350] DEBUG: pid 449, main(), line 1876: Sending “1” (OK) to parent via fd=7
            [17-Mar-2016 22:33:06.346403] DEBUG: pid 448, fpm_unix_init_main(), line 539: I received a valid acknoledge from the master process, I can exit without error
            [17-Mar-2016 22:33:06.346826] DEBUG: pid 449, fpm_children_make(), line 421: [pool www] child 450 started
            [17-Mar-2016 22:33:06.347222] DEBUG: pid 449, fpm_children_make(), line 421: [pool www] child 451 started
            [17-Mar-2016 22:33:06.347245] DEBUG: pid 449, fpm_event_loop(), line 371: 5992 bytes have been reserved in SHM
            [17-Mar-2016 22:33:06.347250] NOTICE: pid 449, fpm_event_loop(), line 372: ready to handle connections
            [17-Mar-2016 22:33:06.347258] DEBUG: pid 449, fpm_systemd_heartbeat(), line 68: have notify start to systemd
            [17-Mar-2016 22:33:06.347262] NOTICE: pid 449, fpm_systemd_heartbeat(), line 75: systemd monitor interval set to 10000ms
            [17-Mar-2016 22:33:07.348356] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:08.349505] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:09.350674] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:10.351851] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:11.352985] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:12.354095] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:13.355262] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:14.356426] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:15.357621] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:16.357769] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:17.358838] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:17.586789] WARNING: pid 449, fpm_stdio_child_said(), line 191: [pool www] child 450 said into stderr: “DEBUG: main(), line 1926: Primary script unknown”
            [17-Mar-2016 22:33:17.586839] DEBUG: pid 449, fpm_event_loop(), line 424: event module triggered 1 events
            [17-Mar-2016 22:33:18.359726] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:19.360894] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:20.362069] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:21.363260] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:22.364463] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:23.365632] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:24.366834] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:25.368035] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:26.368346] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:27.369481] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1
            [17-Mar-2016 22:33:28.370608] DEBUG: pid 449, fpm_pctl_perform_idle_server_maintenance(), line 379: [pool www] currently 0 active children, 2 spare children, 2 running children. Spawning rate 1

            I also enabled access logging, /usr/log/www.access.log contains:

            127.0.0.1 – 17/Mar/2016:22:38:00 +0000 “GET /phpinfo.php” 404

          4. Would it be possible to access the /status page? php seems to be able to create this with the pm.status_path = /status config in /etc/php/7.0/fpm/pool.d/www.conf?

            I tried adding the status page as index page to the Caddyfile, but it looks like it’s not forwarded to php
            “`
            fastcgi / 127.0.0.1:9000 php {
            env PATH /bin
            index status
            }
            “`

          5. Omg, the caddy issue queue contained the solution:
            https://github.com/mholt/caddy/issues/435#issuecomment-164365541
            I was trying this from the home folder of the root user, and no matter what rights and group I gave the file it didn’t work. Trick was to test this outside of the root’s home folder.

            Still can’t believe php’s logs where so uninformative. Did learn a lot though! Thanks for all the help.

  2. MathiasB I figured out an error in the Caddyfile but don’t have enough time right now to open a PR on the original blockpost. In order to get the Calendar working (also in the web UI), the rewrite regex must include “dav” for OC 9:

    # remove trailing / as it causes errors with php-fpm
    rewrite {
    r ^/remote.php/(webdav|caldav|carddav|dav)(/?)$
    to /remote.php/{1}
    }

    rewrite {
    r ^/remote.php/(webdav|caldav|carddav|dav)/(.+)(/?)$
    to /remote.php/{1}/{2}
    }

    Otherwise CalDAV calendar lookup in the calendar-app will fail.

    1. As I pointed out, this blogpost came a bit to early:

      Update: Caddy’s current release doesn’t support WebDav yet, resulting in the desktop/mobile clients not working. Support for this is already included in the development branch, so you can either wait a until the next release, or compile Caddy yourself.

      Caddy doesn’t yet support all features for serving ownCloud. But luckily, they are already implemented and pushed to the master branch. So it’s only a matter of time before a new version of Caddy is officially released. You can always compile your own version of Caddy in the meantime.

    1. Yes, I’m quite sure. Caddy doesn’t actually handle WebDav, but it forwards the requests to PHP. Before the changes (which aren’t in release yet), Caddy errored on these WebDav requests. So by “support” we just mean, don’t error on WebDav and pass them through…

  3. First, thank you very much for the guide, it has been very helpful! One item of note, if you’re running your setup on a Linux based system and do not want to run caddy as root, you can do the following command:

    sudo setcap cap_net_bind_service=+ep //caddy

    So, if your caddy bin file is located in /home/myusername/bin then the command would be:

    sudo setcap cap_net_bind_service=+ep /home/myusername/bin/caddy

    Additional note, this binds 80 and 443 to that application. If you update the application to a new caddy version then you’ll need to rerun this command.

    1. Thanks for the additions. I hadn’t included ‘how to run’ Caddy in the article, since it’s out of it’s scope (imo). I have other guides on my blog about how to run Caddy as a service, in which I explain the setcap.

  4. Because WebDav might not work yet with Caddy 0.9.1, I’m now trying to run it [from source](https://github.com/mholt/caddy/#running-from-source). The weird thing is that the new dev version can’t seem to parse the Caddyfile from this post.
    “`
    $ caddy_backup –version
    Caddy 0.9.1 (+e8e5595 Mon Sep 05 04:12:57 UTC 2016)
    $ caddy_backup
    Activating privacy features… done.
    https://owncloud.peteruithoven.nl
    http://owncloud.peteruithoven.nl
    “`
    “`
    $ caddy –version
    Caddy (untracked dev build)
    peteruithoven@peteruithoven:/var/www$ caddy
    Activating privacy features… done.
    2016/09/11 03:45:45 Caddyfile:34 – Parse error: Wrong argument count or unexpected line ending after ‘status’
    “`
    I’ve only changed the domain name (line 1) in the file.

    This probably has something to do with: https://github.com/mholt/caddy/issues/1092#issuecomment-244966181
    I’ll look into this further tomorrow.

  5. Could it be that the second rewrite to remove trailing /’s, the second group should be made non greedy?

    # remove trailing / as it causes errors with php-fpm
    rewrite {
    r ^/remote.php/(webdav|caldav|carddav|dav)(/?)$
    to /remote.php/{1}
    }

    rewrite {
    r ^/remote.php/(webdav|caldav|carddav|dav)/(.+?)(/?)$
    to /remote.php/{1}/{2}
    }

    It seems to fix the calendar and contacts app in OC 9

Leave a comment

Your email address will not be published. Required fields are marked *