I have always loved the convenience and versatility of cloud based services, but never really liked the idea of not “owning” the files on them. I also found myself becoming increasingly uneasy with Google over the years, and wanted to migrate away from their platform as soon as possible. In general, I don't really like the idea of getting locked into a single ecosystem, whether it be Apple/iCloud, Microsoft/O365, or Google/GSuite.

That's why I was so excited when I heard about Nextcloud, a self hosted cloud platform that is practically a drop in replacement for the services mentioned above. In fact in a lot of ways, Nextcloud offers more functionality. Nextcloud is FOSS, anyone can write a useful application and share it with the world. I am able to do things with Nextcloud that I was never able to achieve through an off the shelf cloud suite.

There are many ways to use Nextcloud. You could set it up as an organization, and have a self hosted array of cloud servers for your company. Personally, I treat Nextcloud as a “life dashboard” of sorts. If done right, you can make Nextcloud a one stop shop for productivity, organization, and personal management. Here are some of the things one can use Nextcloud for:

  • Calendar
  • Contacts
  • Email
  • Task Manager
  • File Storage
  • Real Time Phone Tracking
  • SMS Backups
  • Recipe Book
  • Notes
  • Bookmarks

And much more!

I have been using Nextcloud for a couple years now, and since then, I have become more familiar with Docker. The first time I set up Nextcloud, I used a pretty sketchy install script. This worked, but I was never really sure exatcly how things were set up. If you know me, that's not how I roll. I realized the time had come to redo things with Docker.

Getting a Server

While you could totally host this out of a spare PC in your house, I chose to go with a VPS for a couple reasons:

  • Reliable uptime
  • Better up/down speeds
  • Automated backups
  • Easily transfer to another VPS

I chose to go with an Ubuntu 18.04 VPS from DigitalOcean, because they have been fantastic in the past. Theoretically this should work anywhere.

One might also ask why I chose to use docker in this situation. I had multiple servers at DO all running separate services. With docker, I would be able to downsize to a single server, and run multiple containerized services side by side, much like with my Home Media Server build. This would allow me to easily deploy this configuration on any machine, and save me the hassle of setting up and reverse-proxying multiple services running on bare metal.

Setting up Docker

Run the following command to get the docker tools installed on your system. I would recommend making a new user so you don't do the entire tutorial as root.

sudo apt install docker.io docker-compose

And give your user permission to execute docker commands without a password (requires you to sign in again)

sudo usermod -aG docker ${USER}

Deploying the Services

First, Let's get our compose file out of the way. Make a new directory that you want to use for your docker services and create a file called docker-compose.yaml.

Here is my docker-compose.yaml, where I have declared the traefik, ncdatabase, and nextcloud services. ncdatabase is a MariaDB that will act as the database used by Nextcloud. Then, the nextcloud will host our main Nextcloud instance. Finally, I am using the traefik provide SSL certificates and reverse proxy incoming connections to the correct service.

version: '3'

services:

  traefik:
    image: "traefik:v2.0.5"
    container_name: "traefik"
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge=true"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myhttpchallenge.acme.email=yourname@yourdomain"
      - "--certificatesresolvers.myhttpchallenge.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "$HOME/data/letsencrypt:/letsencrypt"
    networks:
      - traefik

  ncdatabase:
    image: mariadb
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: always
    volumes:
      - /home/gideon/data/nextcloud/db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=DECLARE_THIS_VALUE
      - MYSQL_PASSWORD=DECLARE_THIS_VALUE
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
    labels:
      - "traefik.enable=false"
    networks:
      - traefik

  nextcloud:
    image: nextcloud
    ports:
      - 8080:80
    volumes:
      - NEXTCLOUD_DIRECTORY:/var/www/html
    restart: always
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.mydomain.com`)" # This is the domain you want nextcloud to be reached on
      - "traefik.http.routers.nextcloud.entrypoints=websecure"
      - "traefik.http.routers.nextcloud.tls.certresolver=myhttpchallenge"
      - "traefik.frontend.redirect.permanent=true"
      - "traefik.frontend.redirect.regex=https://(.*)/.well-known/(card|cal)dav" # These last few labels were added to silence some warnings
      - "traefik.frontend.redirect.replacement=https://$$1/remote.php/dav/"      # They may or may not be necessary
      - "traefik.frontend.headers.customResponseHeaders=Strict-Transport-Security:15552000"
    networks:
      - traefik

networks:
  traefik:
    external: true

Note: You must make sure you have A records pointing your chosen domain to the IP address of your server. With DigitalOcean, this can be set up in the Networking dashboard.

Nextcloud should have generated some files. In the main Nextcloud directory, cd into config/ and edit the config.php file. Edit or add the values shown below to reflect your configuration.

'trusted_domains' =>
array (
  0 => 'nextcloud.mydomain.com',
  1 => 'localhost',
),
'trusted_proxies' =>
array (
  0 => '<IP ADDRESS OF DOCKER>',
),
'forwarded_for_headers' =>
array (
  0 => 'HTTP_X_FORWARDED_FOR',
),

'overwritehost' => 'nextcloud.mydomain.com',
'overwrite.cli.url' => 'http://nextcloud.mydomain.com',
'overwriteprotocol' => 'https',

trusted_domains allows Nextcloud to be reached at your chosen domain. trusted_proxies allows Nextcloud to properly detect connecting IP addresses that are being reverse-proxied by traefik. forwarded_for_headers was added to get rid of an error about invalid HSTS headers. Finally, the last three lines enable HTTPS redirects. Without these lines, sync clients will be unable to correctly authenticate with the server.

To bring up the stack, run docker-compose up -d, or docker-compose up if you want to see the logs in your terminal. If all goes correctly, you should be able to access your cloud instance at the domain you selected.

Setting up Nextcloud

At this point, you should be looking at a stock Nextcloud welcome screen. First things firs, create yourself a non-admin user we can use after we get everything set up. This way we can delegate mission critical tasks to the admin account with a very strong password, and use our own account without access to admin settings.

One of the main features of Nextcloud I use is the sync clients. I have one on every device. On Arch linux, you can download the nextcloud-client package. Open it up and log into the server using your newly created user account. From here you can select a local folder on your machine and map it to a remote folder on your Nextcloud.

I tend to keep my Nextcloud folder structure almost a 1 to 1 match with the folders in /home of my computers. That makes syncing the folders absurdly easy, ~/Documents gets mapped to Documents, etc.

This instant synchronization allows me to edit a file on my laptop in one room, and resume editing the same file moments later on my desktop or phone. It's almost like I have one /home directory synced across all my devices. Of course you might not want to sync everything. I found that nextcloud does not play nice with git directories, and will mess with the local branch. I exclude mission critical local repositories from syncing so I can manage them through github.

Conclusion

This guide showed you how to spin up the bare minimum Nextcloud instance using Docker. In my next guide, I'll show you how to flesh out this instance, and what tools to use to make your digital life completely seamless.