Skip to content

Installing Nextcloud on F2-221

A follow-up post to my tutorial Docker, Reverse Proxy and Services on F2-221. In this tutorial I’ll cover the steps of installing Nextcloud on the TerraMaster F2-221 via docker-compose, using Traefik as a reverse proxy. The following steps depend on the setup explained in the above mentioned tutorial. If you just blindly copy & paste the steps you’ll most likely end with a non functional nc instance.


Nextcloud offers a lot of options via docker-compose. You’ll find a complete list on the docker hub page. For this setup we will use apache with mariadb. Because of limited RAM on the F2-221 (I’m using the stock 2 GB), I decided against using Redis or any other cache. I’m using this instance for a small user base, so the performance is absolutely fine.

Create a directory and dive into docker-compose.yml with vi.

version: '2'


    image: mariadb
    restart: always
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
      - db:/var/lib/mysql
      - MYSQL_ROOT_PASSWORD=sqlrootpw123
      - MYSQL_PASSWORD=nextcloud
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - internal
      - "traefik.enable=false"

    image: nextcloud
    restart: always
      - 8080:80
      - app:/var/www/html
      - config:/var/www/html/config
      - data:/var/www/html/data
      - MYSQL_PASSWORD=nextcloud
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_HOST=db
      - db
      - web
      - internal
      - "traefik.enable=true" 
      - "``)" 
      - ""
      - ""
      - ",nextcloud-sec"
      - "traefik.http.middlewares.nextcloud-dav.replacepathregex.regex=^/.well-known/ca(l|rd)dav"
      - "traefik.http.middlewares.nextcloud-dav.replacepathregex.replacement=/remote.php/dav/"
      - "traefik.http.middlewares.nextcloud-sec.headers.framedeny=true"
      - "traefik.http.middlewares.nextcloud-sec.headers.sslredirect=true"
      - "traefik.http.middlewares.nextcloud-sec.headers.browserXssFilter=true"
      - "traefik.http.middlewares.nextcloud-sec.headers.contentTypeNosniff=true"
      - "traefik.http.middlewares.nextcloud-sec.headers.stsIncludeSubdomains=true"
      - "traefik.http.middlewares.nextcloud-sec.headers.stsPreload=true"
      - "traefik.http.middlewares.nextcloud-sec.headers.stsSeconds=31526000"
      - "traefik.http.middlewares.nextcloud-sec.headers.customFrameOptionsValue=SAMEORIGIN"

    external: true
    internal: true

Dayum, that’s much text, duuude!

Ok, so let’s break it down. As mentioned above, we have 2 services here. db for MariaDB and app for Nextcloud. No surprise yet. We can use docker-compose labels to control the interconnection between any container and Traefik. So, this is pretty handy. We don’t need to tinker with Traefik again and again – just pass the parameter to docker and you’re fine.

The fun part here is having 2 networks in this file: the good ol’ known web and a new one called internal. The key difference is: the container connected to web can be seen by traefik and any other container on the same bridge – whereas internal is explicitly just between db <-> app available.

Even if a container on the web bridge is compromised, there is no possibility to get to the db. 🤙

MariaDB will automatically generate the user and database with the credentials listed in its environment section. The environment info on “app” presets the correct sql user credentials to the nextcloud installation. No need to manually type in any of this.

As you might have guessed already, the label called traefik.enable=false disables the database from being exposed by/through Traefik.

The nextcloud container “app” has a lot of labels assigned. You should change the Host again accordingly to your nextcloud (sub)domain. The rest ensures that caldav and carddav (Calender + Contacts) sync will work as intended, as well as having a hardened SSL config.

Start it up

Run the project by executung docker-compose up -d. Docker will download the needed images, creates the projects internal network bridge and starts both containers afterwards. After a short period of time, go and visit your nextcloud instance by entering into your browser.

A big blue-ish install wizard will appear on your monitor:


Choose some secure admin credentials and start the installation. Nextcloud will automatically connect to the mariadb service through the internal network with the account info we’ve set up in the docker-compose file. Fantastic, isn’t it?

But wait… uhm… wat?

Did you notice something? Right, no need to request a SSL certificate and set it up manually. No need to set any forwardings or whatever. Just plug and play, start and use, … you call it.

Traefik did all its magic in the background, registering the router and services by observing the docker socket. Additional instructions came with the labels of the nextcloud service.

After the nextcloud install wizard made its installations you can directly use the cloud.

Activating the cron for maintenance work

One last thing: we’ll set up a cronjob for running the background tasks. For this we’ll use crontab -e on the NAS (not inside the container!) to set it up. Add the following line to the end of the file:

*/5 * * * * docker exec -u www-data nextcloud_app_1 php cron.php

Replace the name of the container (here: “nextcloud_app_1”) accordingly. You can list all running containers by executing docker ps.


We’ve set up a new docker-compose project with an additional internal network bridge, used for secure communication between the database and the app/webserver. Traefik did all needed changes in the background. MariaDB and Apache get set up with preset credentials. Cron takes care of background jobs.

A ready to use cloud is built within a few minutes – fully ssl secured with a let’s encrypt cert. Have a lot of fun with it – and don’t forget about the nextcloud appstore!

Encountered any problems? Write in the comment section down below and I get back to you asap.

Leave a Reply

Your email address will not be published. Required fields are marked *