This is a guide to deploying Nextcloud behind a Caddy reverse proxy, both running in Docker containers (an official Nextcloud one and a caddy-docker-proxy one), with the goal of implementing as much as possible via docker-compose files. This is much more difficult than it should be, for a variety of reasons:
-
As with Docker versions of software in general, documentation of the software does not always apply to the Docker versions, and the Docker documentation does not always include the Docker equivalent ways of doing things.
-
Docker images do not always expose the desired configuration knobs of the underlying software.
-
Nextcloud requires special configuration to run correctly behind a reverse proxy (and again, some of the instructions for this configuration requires modification for Dockerized versions of Nextcloud and the reverse proxy).
-
Workarounds are necessary for various outstanding issues (and again, these workarounds have to be modified to work in a Dockerized configuration).
This guide documents in detail one method of deploying and configuring Nextcloud and Caddy in Docker containers. The guide consists primarily of instructions and configuration files; for explanations of some of the problems encountered and solutions thereto, follow the links provided. The customization in this guide is almost entirely for the Nextcloud container; the Caddy reverse proxy one is deployed in its basic, standard form, and can be used to reverse proxy additional services as desired.
This guide assumes that ports 80 and 443 on the host are available for use by Caddy. System commands used in this guide are for Debian Stable, but they should be easily adaptable to other distributions.
To make the Nextcloud instance publicly available, a valid domain name should be pointed at the server on which the Docker containers will be run. This can be a second level domain name (example.com
), a third level one (nextcloud.example.com
), and even one handled by a dynamic DNS (DDNS) provider (as long as the second level domain in question has been added to the public suffix list - see here and here).
It can be useful, particularly if multiple services or websites are to be made available behind the reverse proxy, to utilize a DNS provider that offers wildcard functionality, where all subdomains of the registered domain will automatically resolve to the IP address of the registered domain, i.e., if 'mydomain.example.com' is registered, Nextcloud and Caddy can be configured to be available at 'nextcloud.mydomain.example.com' without any further domain registration or configuration. Similarly, additional services can be deployed under other subdomains of 'mydomain.example.com' (e.g. 'web.mydomain.example.com', 'mail.mydomain.example.com'), again without any further domain registration or configuration.
Some DDNS providers do not offer wildcard support, or only offer it on a paid service tier, but at the time of this writing, the free DDNS provider Duck DNS provides automatic wildcard support out of the box. This guide will utilize the DDNS name example.duckdns.org
, and make the Nextcloud instance available at nextcloud.example.duckdns.org
.
Caddy provides "Automatic HTTPS". This means exactly what it says: as long as a valid domain name is used, Caddy will automagically implement HTTPS on its own, with no user configuration required. When the steps in this guide are completed, the Nextcloud instance will be available via HTTPS at https://nextcloud.example.duckdns.org
, with no further configuration required.
Install Docker:
# apt install docker.io
Create the Docker network that the Nextcloud and Caddy containers will use to communicate with each other:
# docker network create caddy --subnet=172.16.0.0/24
Although manual specification of IP subnets and addresses is not really in the spirit of Docker, it is sometimes necessary, or at least convenient; in our case, it enables us to set the Nextcloud Docker container's TRUSTED_PROXIES
environment variable (see below).
Create the following file as something like $HOME/docker/nextcloud/docker-compose.yml
. It is a version of the official Nextcloud "Base version - apache" docker-compose file, modified for Caddy integration:
version: '2'
# most of this is taken from here: https://github.com/nextcloud/docker / https://hub.docker.com/_/nextcloud
# changes and additions are documented in the comments below
# see also: https://github.com/nextcloud/docker/issues/1414
services:
db:
# see: https://github.com/nextcloud/docker/issues/1536
image: mariadb:10.5
restart: always
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
volumes:
- db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=<password1>
- MYSQL_PASSWORD=<password2>
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
networks:
caddy:
ipv4_address: 172.16.0.8
app:
image: nextcloud:stable
restart: always
networks:
caddy:
ipv4_address: 172.16.0.7
# see: https://github.com/nextcloud/documentation/blob/master/admin_manual/configuration_server/reverse_proxy_configuration.rst
labels:
caddy: nextcloud.example.duckdns.org
caddy.reverse_proxy: "{{upstreams}}"
# see: https://github.com/lucaslorentz/caddy-docker-proxy/issues/114
caddy.header: /*
# see: https://docs.nextcloud.com/server/23/admin_manual/installation/harden_server.html#enable-http-strict-transport-security
caddy.header.Strict-Transport-Security: '"max-age=15552000;"'
# see: https://docs.nextcloud.com/server/23/admin_manual/issues/general_troubleshooting.html#service-discovery
# https://github.com/lucaslorentz/caddy-docker-proxy/issues/222
caddy.rewrite_0: /.well-known/carddav /remote.php/dav
caddy.rewrite_1: /.well-known/caldav /remote.php/dav
ports:
- 8080:80
volumes:
- nextcloud:/var/www/html
environment:
- MYSQL_PASSWORD=<password2>
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_HOST=db
# See: https://hub.docker.com/_/nextcloud/
- APACHE_DISABLE_REWRITE_IP=1
# See: https://github.com/nextcloud/documentation/issues/7005
# and: https://old.reddit.com/r/NextCloud/comments/s3skdn/nextcloud_behind_caddy_as_a_reverse_proxy_using/hsnj5wh/
- TRUSTED_PROXIES=172.16.0.6
links:
- db
cron:
# Nextcloud cron functionality with Docker deployments is not well documented:
# https://github.com/nextcloud/docker/blob/master/.examples/docker-compose/with-nginx-proxy/mariadb/apache/docker-compose.yml#L39
# https://github.com/nextcloud/docker/blob/master/.examples/docker-compose/insecure/mariadb/apache/docker-compose.yml#L35
# https://github.com/nextcloud/docker/issues/1695
# https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/background_jobs_configuration.html
image: nextcloud:stable
restart: always
volumes:
- nextcloud:/var/www/html
networks:
caddy:
ipv4_address: 172.16.0.9
entrypoint: /cron.sh
depends_on:
- db
volumes:
db:
nextcloud:
networks:
caddy:
external: true
The above file specifies the Nextcloud Docker stable
image tag. There are actually dozens of available tags to choose from. For the purposes of this guide, one of the apache
, rather than one of the fpm
, tags should be chosen (i.e., the tag should either contain apache
or contain neither apache
nor fpm
). Beyond that, any tag should work.
In general, the more specific a version is specified in the configuration, the less the likelihood of something breaking on update, at the price of not receiving various improvements and enhancements. (This is true for all containers, but is particularly significant for software as complex and as rapidly changing as Nextcloud. See here for Nextcloud's explanation of its release channels.)
Set the password environment variables to strong random passwords. The two references to MYSQL_PASSWORD
must contain the same value, and MYSQL_ROOT_PASSWORD
should contain a different value. (They are only used internally, and will not be needed anywhere outside this file.)
Then, in $HOME/docker/nextcloud/
, run:
# docker-compose up -d
Create the following file as something like $HOME/docker/caddy/docker-compose.yml
:
version: "3.7"
services:
caddy:
# see here for guidance on which image / tag to choose:
# https://github.com/lucaslorentz/caddy-docker-proxy#docker-images
image: lucaslorentz/caddy-docker-proxy:2.4.0
ports:
- 80:80
- 443:443
environment:
- CADDY_INGRESS_NETWORKS=caddy
networks:
caddy:
ipv4_address: 172.16.0.6
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- caddy_data:/data
restart: unless-stopped
networks:
caddy:
external: true
volumes:
caddy_data: {}
Then, in $HOME/docker/caddy/
, run:
# docker-compose up -d
If everything has worked correctly, it will now be possible to finish the Nextcloud installation by running the Installation Wizard by navigating to https://nextcloud.example.duckdns.org
and following the prompts. (If the database configuration via the Docker environment variables has worked correctly, then the "Storage and Databases" choices will not be available; if they are, then something has gone wrong with the configuration.)
Navigate to https://nextcloud.example.duckdns.org/settings/admin/
, and adjust the following configuration settings:
- Use
cron
for background jobs - Follow the directions to configure an email server
There are a few remaining configuration settings that should be set which can only be set by directly editing Nextcloud's config.php
file (documented here), or via the occ
command (see below), and cannot (currently) be set using Docker. The config.php
file is located (in Debian, when following this guide) at /var/lib/docker/volumes/nextcloud_nextcloud/_data/config/config.php
. Edit this file and set the following values:
'default_phone_region' => '<ISO 3166-1 country code>'
(e.g.'US'
- see here)
To run the occ
command inside the docker Nextcloud instance, run the following on the host system:
docker exec -ti --user www-data <nextcloud_container_name> /var/www/html/occ <occ parameters>
where nextcloud_container_name
is the name of the Nextcloud container (e.g., nextcloud_app_1
), and occ parameters
are the desired occ parameters.
To use the occ
command to set Nextcloud configuration values, see the occ
documentation.
Nextcloud shows a health check ("Security & setup warnings") at https://nextcloud.example.duckdns.org/settings/admin/overview
. If everything in this guide has worked, there should be only one warning shown:
Module php-imagick in this instance has no SVG support. For better compatibility it is recommended to install it.
There is considerable controversy over this issue, but from a security perspective, it is apparently preferable to stick with the default configuration and ignore this message.
To update the containers, run (in the same directory as the appropriate docker-compose.yml
file)
docker-compose pull && docker-compose up -d
This will update the containers to the latest versions (of the specified image tags). This can be automated with solutions such as Watchtower. General information on the maintenance of Docker containers and images, including the pruning of unused images and containers, is readily available online; see, e.g., here, here, and here.