Nextcloud is an awesome self-hosted cloud-storage service. This is a guide for running Nextcloud using Docker, Traefik, and Let’s Encrypt.
Why should you use Docker to deploy Nextcloud?
Well- Have you ever tried setting up a working Nextcloud instance just by using an Apache or NGINX web server? That itself probably isn’t that hard but the configuration you have to do after installing Nextcloud is. There’s this handy dandy feature under Settings -> [Administration] Overview that shows you what steps you need to take to make your Nextcloud instance secure, fast, and reliable. And if you’ve just set up Nextcloud- oh boy- there’s probably a lot to do.
Since Docker images not only come with the software installed already, but also with a finished and optimized configuration, there’s pretty much nothing more to do once you’ve got your Nextcloud container up and running.
Also, probably because Nextcloud is such complex software, I was never really able to optimize my manual Nextcloud instance as well as the Docker image. I always noticed performance issues or other kinds of things that just didn’t work or didn’t work as expected.
Why should you run the instance behind Traefik?
Traefik acts as the “reverse proxy” in this configuration. That means we don’t have to expose the web server in the container publicly on the web, which has a couple of advantages. One of them being that you don’t have to run it on a different port or something when dealing with several services on one server.
An example of that would be my server. I don’t just run my blog but also a homepage on that server, which are two completely separate web apps, code-bases, and Docker containers. I don’t have to use weird ports behind the web address though, because everything is routed through Traefik.
I’m also able to easily rewrite HTTP headers, implement middlewares, and more.
Why specifically Traefik and not other reverse proxies (like NGINX or Apache)?
Well- that’s probably a question for a separate article. One of the reasons is certainly the way it works hand-in-hand with Docker and the fact that it needs almost no configuration (in the form of config files).
But you’ll see all of this yourself- now.
Step 1: Setting up Nextcloud, MariaDB, and Redis
Of course, you can also use MySQL instead of MariaDB.
docker-compose.yml for Nextcloud, MariaDB, and Redis:
First, we’ll set up a Docker Compose stack for Nextcloud, MariaDB, and Redis:
version: '3.7' services: nextcloud-db: image: mariadb:latest container_name: nextcloud-db command: --transaction-isolation=READ-COMMITTED --log-bin=ROW --skip-innodb-read-only-compressed restart: unless-stopped volumes: - ./mysql-data:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD=test # MYSQL ROOT PASSWORD - MYSQL_PASSWORD=test # PASSWORD OF MYSQL USER - MYSQL_DATABASE=nextcloud # MYSQL DATABASE - MYSQL_USER=nextcloud # MYSQL USERNAME networks: - default nextcloud-redis: image: redis:alpine container_name: nextcloud-redis hostname: nextcloud-redis restart: unless-stopped command: redis-server --requirepass test # REPLACE WITH REDIS PASSWORD networks: - default nextcloud-app: image: nextcloud container_name: nextcloud-app restart: unless-stopped depends_on: - nextcloud-db - nextcloud-redis environment: - REDIS_HOST=nextcloud-redis - REDIS_HOST_PASSWORD=test # REPLACE WITH REDIS PASSWORD - MYSQL_HOST=nextcloud-db - MYSQL_USER=nextcloud # MYSQL USERNAME - MYSQL_PASSWORD=test # PASSWORD OF MYSQL USER - MYSQL_DATABASE=nextcloud # MYSQL DATABASE volumes: - ./nextcloud:/var/www/html labels: - 'traefik.http.routers.nextcloud.entrypoints=http' - 'traefik.http.routers.nextcloud.rule=Host(`cloud.example.com`)' - 'traefik.http.middlewares.https-redirect.redirectscheme.scheme=https' - 'traefik.http.routers.nextcloud.middlewares=nc-header,https-redirect' - 'traefik.http.routers.nextcloud-secure.entrypoints=https' - 'traefik.http.routers.nextcloud-secure.rule=Host(`cloud.example.com`)' - 'traefik.http.middlewares.nc-rep.redirectregex.regex=https://(.*)/.well-known/(card|cal)dav' - 'traefik.http.middlewares.nc-rep.redirectregex.replacement=https://$$1/remote.php/dav/' - 'traefik.http.middlewares.nc-rep.redirectregex.permanent=true' - 'traefik.http.middlewares.nc-header.headers.customFrameOptionsValue=SAMEORIGIN' - 'traefik.http.middlewares.nc-header.headers.customResponseHeaders.Strict-Transport-Security=15552000' - 'traefik.http.routers.nextcloud-secure.middlewares=nc-rep,nc-header' - 'traefik.http.routers.nextcloud-secure.tls=true' - 'traefik.http.routers.nextcloud-secure.tls.certresolver=letsencrypt' - 'traefik.http.routers.nextcloud-secure.service=nextcloud' - 'traefik.http.services.nextcloud.loadbalancer.server.port=80' - 'traefik.http.services.nextcloud.loadbalancer.passHostHeader=true' networks: - proxy - default networks: proxy: external: true
Important: Replace “cloud.example.com” with the (sub)domain you want to run your cloud on and replace the passwords with secure ones (see comments).
I started with the Nextcloud, MariaDB, and Redis Docker Compose stack because some of you may have already set up Traefik and created a network for it. In that case, you can just connect the containers to it by changing “proxy” to your network name. If you haven’t created a Docker network for Traefik yet, don’t worry, we’re going to do that in Step 2.
There’s not much to explain here, except maybe what all those Traefik labels mean. Basically, there’s a lot to do when a request to your cloud domain comes in.
The first thing you want to do is upgrading the connection from HTTP to HTTPS. This is simply done by creating a middleware using the container labels (yes, that’s possible). Eventually, the incoming traffic gets routed to a “service”, which in this case is port 80 within the container.
You don’t additionally have to expose the port on the host machine. Since the Traefik container connects to the same Docker network as the Nextcloud container, they can communicate on that port (using that network).
Then there’s a bunch of middlewares for (over)writing and passing headers of requests (I just followed the instructions on the Nextcloud overview and the docs for optimizing those). See “Hardening and security guidance” for reference.
Step 2: Setting up Traefik
The first thing that’s important, as mentioned above, is creating a Docker network that all the containers that you want to run behind Traefik connect to. This is done so we can route the traffic internally as opposed to exposing ports on the host machine.
Creating the network
You can create a new Docker network using:
$ docker network create proxy
You can name it anything you want. For consistency with my example stack above, I call it “proxy”.
docker-compose.yml for Traefik
version: '3.7' services: traefik: image: traefik:latest container_name: traefik restart: unless-stopped ports: - '80:80' - '443:443' volumes: - ./traefik/config/traefik.yml:/traefik.yml - ./traefik/certificates/acme.json:/acme.json - /var/run/docker.sock:/var/run/docker.sock networks: - proxy networks: proxy: external: true
For Traefik we obviously have to expose the ports 80 and 443 to the host machine to make the HTTP and HTTPS traffic that comes in on the host machine go straight to the Traefik container.
Also, we have to create 2 volumes for a) storing a minimal Traefik config file and b) storing our SSL certificates. We’ll later set up Let’s encrypt (in the Traefik config file) to obtain free SSL certificates for our Nextcloud, which is recommended if we want to use HTTPS.
Step 3: Configuring Traefik
Last but not least, we have to create our configuration file for Traefik. Technically this is not necessary and we could do everything using labels. But especially for people who want to put more behind Traefik than just one service, this can make things cleaner and a lot easier to read.
Start by creating a folder called
traefik in the same directory as the
docker-compose.yml (for Traefik) and a subdirectory within that folder called
config then create a file called
traefik.yml and paste the following:
api: dashboard: false insecure: false entryPoints: http: address: ':80' https: address: ':443' providers: docker: network: proxy certificatesResolvers: letsencrypt: acme: email: your@email storage: acme.json httpChallenge: entryPoint: http
This just creates our certificate resolver, which is automatically going to obtain and renew the certificates. It also deactivates the internal dashboard (if you want to enable it though, feel free to do so).
If it doesn’t work, make sure the certificates folder has the permissions
600. Change the permissions by doing
chmod -R 600 certificates/.
Both docker-compose.yml files have to be in different folders for Docker Compose to use the correct one. Start both of them (separately) by running
docker-compose up -d in both directories.
You can now open up your browser and go to the domain of your Nextcloud. You should be greeted with the initial setup screen.