I’ve set up my private cloud using Nextcloud. Because I’m a huge fan of Docker, I decided to run Nextcloud as a Docker container. Luckily, there’s an official Docker image and they also provide examples on how to run Nextcloud with a standalone database using Docker Compose.

Updated in November 2022 to include the latest Docker images


a) Basic setup

The basic setup without subdomain, without TLS and without a reverse proxy is super easy. The corresponding docker-compose.yml looks like this:

---
version: "3"
services:
  nextcloud:
    image: "nextcloud:25.0.1-apache"
    ports:
      - "8080:80"
    restart: always
    volumes:
      - nextcloud:/var/www/html
    environment:
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=<MYSQL_PASSWORD>
      - MYSQL_HOST=mariadb
      - NEXTCLOUD_ADMIN_USER=<NEXTCLOUD_ADMIN_USER>
      - NEXTCLOUD_ADMIN_PASSWORD=<NEXTCLOUD_ADMIN_PASSWORD>
  mariadb:
    image: "mariadb:10.9.4-jammy"
    command: "--transaction-isolation=READ-COMMITTED --binlog-format=ROW"
    restart: always
    volumes:
      - db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=<MYSQL_ROOT_PASSWORD>
      - MYSQL_PASSWORD=<MYSQL_PASSWORD>
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
volumes:
  nextcloud:
  db:

You can then start your Nextcloud setup using the following shell command:

docker-compose up -d

Use the following command to see a continuous log stream of your Nextcloud and MariaDB Docker contains on stdout. You can press Ctrl + C at any time to stop the log output (this will not shutdown your Nextcloud container):

docker-compose logs -f

b) Advanced setup

Now, suppose we want to add a reverse proxy like NGINX in front of Nextcloud because we might want to run several applications behind different subdomains on our server. Furthermore, we want to secure our connection to Nextcloud through TLS because we don’t want that all our private data is transfered unencrypted over the internet:

PlantUML diagram denoting the scenario with Docker

To realize this scenario, we need to make these changes:

  • The Docker containers of NGINX and Nextcloud (+ MariaDB) need to run on the same Docker network so that NGINX can proxy traffic to Nextcloud. I assume that you already have a running NGINX with TLS setup (read this for a how-to). Make sure to use the appropriate network name! When using Docker Compose, the network name is a concatenation of the directory name (where the docker-compose.yml is stored) + the name of the NGINX container + network (each part separated by hyphens). In my case the NGINX is running inside a Docker network called apps_nginx_network.

  • Since the Docker containers of NGINX and Nextcloud are now running on the same network, it is not necessary to expose the port of Nextcloud. For this reason, compared to the basic setup described above, the port exposure (ports: - "8080:80") can be omitted.

  • For security reasons, Nextcloud prints an error message when running on a subdomain. To fix this error, set the environment variable NEXTCLOUD_TRUSTED_DOMAINS to the fully-qualified domain name (FQDN) under which your Nextcloud instance will be served, in this case cloud.example.com.

Resulting configuration docker-compose.yml:

---
version: "3"
services:
  nextcloud:
    image: "nextcloud:25.0.1-apache"
    restart: always
    volumes:
      - nextcloud:/var/www/html
    environment:
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=<MYSQL_PASSWORD>
      - MYSQL_HOST=mariadb
      - NEXTCLOUD_ADMIN_USER=<NEXTCLOUD_ADMIN_USER>
      - NEXTCLOUD_ADMIN_PASSWORD=<NEXTCLOUD_ADMIN_PASSWORD>
      - NEXTCLOUD_TRUSTED_DOMAINS=cloud.example.com
    networks:
      - <NAME_OF_NGINX_DOCKER_NETWORK>
  mariadb:
    image: "mariadb:10.9.4-jammy"
    command: "--transaction-isolation=READ-COMMITTED --binlog-format=ROW"
    restart: always
    volumes:
      - db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=<MYSQL_ROOT_PASSWORD>
      - MYSQL_PASSWORD=<MYSQL_PASSWORD>
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
    networks:
      - <NAME_OF_NGINX_DOCKER_NETWORK>
volumes:
  nextcloud:
  db:
networks:
  <NAME_OF_NGINX_DOCKER_NETWORK>:
    external: true  

After updating your docker-compose.yml, you can restart Nextcloud so that the changes take effect:

docker-compose down
docker-compose up -d

Forward traffic from NGINX to Nextcloud:

Because NGINX and Nextcloud run inside the same Docker network, they can ping / reach eachother through their container names (= their hostnames). That means, in this example the NGINX container can forward traffic to Nextcloud through http://nextcloud:80.

Your NGINX configuration could look similar to this:

...

server {
    listen 443 ssl;
    server_name cloud.example.com;
    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/cloud.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cloud.example.com/privkey.pem;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://nextcloud:80/;
        proxy_set_header    Host                $http_host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    }

    client_max_body_size 10G;
}

...