Skip to content

Nextcloud

Nextcloud is your own cloud for files photos and additional features like contacts, calendar sync etc. This setup is prepared already for audio and video conferencing via Nextcloud Talk. However, this requires a TURN server reachable via the external internet. It is prepared in this docker-compose file but needs network configuration on your router.

Installation

The relevant installation steps comprise setting environment variables in a .env file. Thus, you need to create a hidden file with the name .env (including the '.') containing the following environment variables:

NEXTCLOUD_DB_ROOT_PASSWORD=your_db_root_password
NEXTCLOUD_DB_NAME=your_db_name
NEXTCLOUD_DB_USER=your_db_user
NEXTCLOUD_DB_PASSWORD=your_db_user_password

Additionally, the docker-compose file configures two volumes for the content, Nextcloud is displaying and the Nextcloud application itself (containing also some configurations done via the web frontend). The data and the application is kept even when throwing away the Docker image. The path on the host system is

/srv/docker/nextcloud/app:/var/www/html
/srv/docker/nextcloud/nas:/var/www/data

Navigating to this directory will show all files, Nextcloud uses to display content, thus an existing backup can be restored here (database content needs to be taken care of separately, obviously). I chose the name nas to indicate that I mounted a NAS volume to that directory. Feel free to change...

Configuration

The docker-compose file defines three services: the database, the Nextcloud application and the TURN server (for Nextcloud Talk) and connects them via the corresponding network configuration.

Database

First, let's start with the database definition: it is a pretty simple and straightforward MariaDB definition that

  • defines the docker image to be downloaded from docker hub
  • provides a name for our service (for easier recognition of the container and navigation e.g. in Portainer)
  • reads all required variables from the .env file mentioned above
  • connects the database to the backend network (no need for the database to be addressed from outside)
  nextcloud-db:
    image: jsurf/rpi-mariadb:latest
    container_name: nextcloud-db
    restart: always
    volumes:
      - /srv/docker/nextcloud/mariadb_var:/var/lib/mysql
    environment:
      - TZ=Europe/Berlin
      - PUID=1000
      - PGID=1000
      - MYSQL_ROOT_PASSWORD=${NEXTCLOUD_DB_ROOT_PASSWORD}
      - MYSQL_DATABASE=${NEXTCLOUD_DB_NAME}
      - MYSQL_USER=${NEXTCLOUD_DB_USER}
      - MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
    # innodb format
    command: mysqld --innodb-large-prefix=true --innodb-file-format=barracuda
             --innodb-file-per-table=1
    networks:
      - backend

There are two things worth mentioning: first, we define a volume for the content of the database so that it is kept even when throwing away the Docker image. The volume is mapped to

srv/docker/nextcloud/mariadb_var

Second, we define the innodb format to Barracuda with 4 Byte which is required for Nextcloud to display e.g. emojis. This is done by specifying the relevant options on the command line

# innodb format
command: mysqld --innodb-large-prefix=true --innodb-file-format=barracuda
         --innodb-file-per-table=1

Application

For the Nextcloud application we need to do a little more. However, the first part is still pretty straightforward:

  • defines the docker image to be downloaded from docker hub
  • provides a name for our service (for easier recognition of the container and navigation e.g. in Portainer)
  • creates volumes to map the Nextcloud content as well as the application data into the container keeping its content also when throwing away the Docker image
  • links the application container to the database container
  • and defines to overwrite http by https for each resource
    image: nextcloud:stable
    container_name: nextcloud-app
    restart: always
    volumes:
      - /srv/docker/nextcloud/app:/var/www/html
      - /srv/docker/nextcloud/nas:/var/www/data
    links:
      - nextcloud-db
    environment:
      - OVERWRITEPROTOCOL=https

A little more interesting is the Traefik configuration done via labels. First, we enable Traefik to act as a proxy for this respective service by setting the label traefik.enable to true.

Then we define the web-secure entrypoint for encrypted traffic (there is no need to do that for the unencrypted web entrypoint on port 80, since our Traefik configuration catches and redirects all unencrypted traffic to web-secure on port 443).

Most important is the router configuration which domains Traefik shall route to the container for Nextcloud to serve the content: here all requests directed to the domain as configured in the environment variable NEXTCLOUD_DOMAIN (e.g. nextcloud.example.org).

The encryption via Let's Encrypt is enabled via the corresponding labels with the default resolver. This is being handled via Traefik as defined in the configuration of the Traefik container (via ACME).

Additionally, there are quite some security-relevant recommendations to be met regarding the Nextcloud header configurations. These are met by the headers as configured in the nc-hsts middleware as shown in the code fragment. Similarly, this is the case for the caldav and carddav configuration that also requires a corresponding middleware, nc-redirectin this case. Access to caldav and carddav is permanently redirected to remote.php/dav/ for proper handling here.

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nextcloud.entrypoints=web-secure"
      - "traefik.http.routers.nextcloud.rule=Host(`${NEXTCLOUD_DOMAIN}`)"
      - "traefik.http.routers.nextcloud.tls=true"
      - "traefik.http.routers.nextcloud.tls.certresolver=default"
      # HSTS headers to meet SSL security recommendations for nextcloud
      - "traefik.http.routers.nextcloud.middlewares=nc-hsts, nc-redirect"
      - "traefik.http.middlewares.nc-hsts.headers.stsSeconds=15552000"
      - "traefik.http.middlewares.nc-hsts.headers.stsPreload=true"
      - "traefik.http.middlewares.nc-hsts.headers.stsIncludeSubdomains=true"
      - "traefik.http.middlewares.nc-hsts.headers.forceSTSHeader=true"
      # add 'well-known' redirection for caldav and carddav
      - "traefik.http.middlewares.nc-redirect.redirectregex.regex=https://(.*)/.well-known/(card|cal)dav"
      - "traefik.http.middlewares.nc-redirect.redirectregex.replacement=https://$$1/remote.php/dav/"
      - "traefik.http.middlewares.nc-redirect.redirectregex.permanent=true"

      - "traefik.http.services.nextcloud.loadbalancer.server.port=80"
    networks:
      - traefik_proxy
      - backend

Last, but not least, Nextcloud is connected both to the traefik_proxy network so that is reachable from the internet and the backend network to ensure database connectivity.

TURN server

The coTURN server is required in case clients cannot establish a connection between each other. The coTURN server must, thus, be reachable via the internet. This requires appropriate port forwarding in your router and a suitable firewall configuration.

  coturn:
    image: killua99/coturn
    container_name: nextcloud-turn
    restart: unless-stopped
    ports:
      - "3478:3478"

    command: turnserver --log-file=stdout --external-ip=$$(detect-external-ip)
             --realm=${NEXTCLOUD_DOMAIN} --total-quota=1000 --bps-capacity=0
             --stale-nonce --no-multicast-peers
             --listening-port=3478 --fingerprint
             --use-auth-secret --static-auth-secret=${COTURN_AUTH_SECRET}
             --log-file=/var/log/turnserver.log --verbose
    volumes:
      - /srv/docker/nextcloud/log:/var/log

    networks:
      - traefik_proxy

Network

Last, but not least, we refer to the existing external networks... they need to exist as outlined here.

networks:
  traefik_proxy:
    external:
      name: traefik_proxy
  backend:
    external:
      name: backend