This is an example of a Nextcloud docker image served behind a reverse proxying, using a Caddy docker image. In this example, the Nextcloud app serves a subdomain cloud.exmaple.com of the web root domain example.com. Allowing you to easily spin-up other docker containers under your domain's web root and other subdomains.
First off, thank you, tmo1. This deployment is primarily derived from tmo1's guide here. It in part diverges from tmo1's configuration in that this deployment uses the official image of Caddy rather than caddy-docker-proxy.
This deploment uses the following project structure:
stacks
├── caddy
│ ├── caddy-build
│ │ ├── Caddyfile
│ │ └── Dockerfile
│ ├── caddy-env
│ │ └── .env
│ └── compose.yaml
├── networks
│ ├── mk-networks.sh
│ └── rm-network.sh
└── nextcloud
└── compose.yaml
└── .envwhere stacks represents the root of your docker stack projects. You can consider working on this deployment under stacks in $HOME or alternatively, /opt.
This guide assumes you already have setup a domain name for your server. Consider seeing tmo1's guide here for one solution. In addition, a subdomain (for example, cloud.example.com) is setup for your Nextcloud app.
This guide assumes you have installed docker and docker-compose on your system. Consider using this wiki article on how to install and intially ocnfigure docker for Arch-based systems.
Consider that by default docker images are located in /var/lib/docker/. You may consider moving the data root directory for docker if /var/lib/docker/ doesn't have enough space for your Nextcloud data. You can configure the data root directory in /etc/docker/daemon.json. See this for more information. For a fresh installation of docker, you may need to create the /etc/docker directory and /etc/docker/daemon.json file.
This guide also assumes the user running the command is part of the docker user group or is being run by root (for example, via sudo).
Create a docker network outside of your compose files. This will be used to connect Caddy to the Nextcloud docker app. In your compose files, you will flag the docker networks as being external, which tells docker compose to NOT manage the networks See, for example.
Run mk-networks.sh:
# ./mk-networks.shor
docker network create \
--driver=bridge \
--subnet=172.16.0.0/16 \
--gateway=172.16.0.1 \
nc-proxyConsider creating a second network to allow other apps to connect to Caddy. For example, I'm also running an Nginx docker image to host the web root: example.com. Since Nginx doesn't need a static ip for the reverse proxy like Nextcloud, you can create a second network to benefit from docker's internal networking.
For example, you can create the caddy network by running:
docker network create \
--driver=bridge \
caddyCreate the docker compose file (compose.yaml) for Nextcloud in the nextcloud project folder:
# See https://github.com/nextcloud/docker/?tab=readme-ov-file#running-this-image-with-docker-compose
services:
db:
# Note: Check the recommend version here: https://docs.nextcloud.com/server/latest/admin_manual/installation/system_requirements.html#server
image: mariadb:lts
restart: always
command: --transaction-isolation=READ-COMMITTED
volumes:
- db:/var/lib/mariadb
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
networks:
- backend
# Note: Redis is an external service. You can find more information about the configuration here:
# https://hub.docker.com/_/redis
redis:
image: redis:alpine
restart: always
networks:
- backend
# Nextcloud apache container config:
nc-app:
# Select the Nextcloud image version appropriate for your system
# I used 30.0.13 due to a migration from bare metal to a docker container
# Thus, I needed both systems (old-bare metal and new-docker) to be on the same version.
image: nextcloud:30.0.13-apache
restart: always
# Enable ports for testing
# ports:
# - 8080:80
depends_on:
- redis
- db
volumes:
- nextcloud:/var/www/html
- config:/var/www/html/config
- data:/var/www/html/data
environment:
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_HOST=db
# See https://github.com/nextcloud/docker/?tab=readme-ov-file#using-the-image-behind-a-reverse-proxy-and-specifying-the-server-host-and-protocol
# See also https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/reverse_proxy_configuration.html
- TRUSTED_PROXIES=172.16.0.2
- APACHE_DISABLE_REWRITE_IP=1
networks:
backend:
nc-proxy:
# static ip to allow TRUSTED_PROXIES to be set above
ipv4_address: 172.16.0.3
volumes:
nextcloud:
config:
data:
db:
networks:
backend:
nc-proxy:
external: trueSet the mysql passwords in the .env file:
MYSQL_ROOT_PASSWORD=password1
MYSQL_PASSWORD=password2Update the permissions of .env:
chmod 600 .envStart Nextcloud by running this in the nextcloud project folder:
docker compose up -dFor debugging, consider running the container in the foreground:
docker compose upIf you have access to the host of the Nextcloud app, consider uncommenting the ports section. You can bring up the Nextcloud app, and configure the installation at localhost:8080 before exposing the image to the world wide web. You can also do this step just to verify that you can see the initial install screen.
There are two options for configuring Nextcloud's cron:
Add a cron service to the Nextcloud compose file, for example:
cron:
image: nextcloud:apache
restart: always
volumes:
- nextcloud:/var/www/html:z
# NOTE: The `volumes` config of the `cron` and `app` containers must match
entrypoint: /cron.sh
depends_on:
- db
- redisAlternatively, run a cron on the host machine via docker container exec:
docker container exec --user www-data nextcloud_container_name php /var/www/html/cron.phpFor crontab, an example entry can look like:
*/5 * * * * docker container exec --user www-data nextcloud_container_name php /var/www/html/cron.phpFor systemd-based systems, /etc/systemd/system/nextcloud.service can look like this:
[Unit]
Description=Nextcloud cron.php job
[Service]
User=www-data
ExecCondition=docker container exec --user www-data nextcloud_container_name php occ status -e
ExecStart=docerk container exec --user www-data nextcloud_container_name php -f /var/www/nextcloud/cron.php
KillMode=process
Then create the nextcloudcron.timer per the Admin Manual.
See (Nextcloud's Admin Manual on configuring the background jobs)[https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/background_jobs_configuration.html]
Create the docker compose file (compose.yaml) for Caddy in your caddy project folder:
services:
caddy:
build: ./caddy-build/.
restart: always
ports:
- "80:80"
- "443:443"
env_file:
- ./caddy-env/.env
volumes:
- ./caddy-build/Caddyfile:/etc/caddy/Caddyfile
networks:
nc-proxy:
ipv4_address: 172.16.0.2
caddy:
networks:
nc-proxy:
external: true
caddy:
external: trueNote this includes the second docker network caddy to connect to an Nginx app for the web root. You can remove this network, if you aren't using a similar configuration.
Create the Dockerfile in the caddy-build folder:
FROM caddy:latest
COPY Caddyfile /etc/caddy/CaddyfileCreate the Caddyfile in the caddy-build folder:
{
email {$ACME_EMAIL}
}
# nginx web-root
example.com {
reverse_proxy nginx-app:80
}
# Nextcloud
cloud.example.com {
reverse_proxy nc-app:80
}
Create the .env file in the caddy-env folder:
ACME_EMAIL="email@example.com"
TZ='America/Chicago'TODO: Confirm whether these environment variables are still necessary.
Start Caddy by running this in the caddy project folder:
docker compose up -dFor debugging, consider running the container in the foreground:
docker compose upIf you want to host the web root of your domain, for example, a linktree using Nginx, create another docker compose project:
www
├── build-nginx
│ ├── Dockerfile
│ └── html
│ └── index.html
└── compose.yaml
www can be inside the stacks docker projects folder.
Create the docker compose file (compose.yaml) for Nginx in your www project folder:
services:
nginx-app:
build: ./build-nginx/.
restart: always
# ports:
# - 8080:80
networks:
- caddy
networks:
caddy:
external: trueCreate the Dockerfile in the build-nginx folder:
FROM nginx
COPY --chown=101:101 ./html /usr/share/nginx/htmlYou will need to copy the html data and update the ownership using chown.
Put your static HTML (for example, index.html) in the html folder, which is in the build-nginx folder. For example, you can make the root a linktree. As an alternative, you can make this a wordpress app or the like.
Start Nginx by running this in the www project folder:
docker compose up -d