Introduction
This tutorial will guide you to setup Nextcloud on a server using Docker Compose and S3-compatible object storage as a storage backend. The advantages of using an object storage as opposed to a bigger cloud server or a big volume is that you don't have to reserve and pay for capacity in advance and it can grow organically.
Nextcloud administration is a big topic. This tutorial will focus on using the Hetzner Object Storage as a backend and start a regular installation. This can be improved and extended by following the official Nextcloud documentation at https://docs.nextcloud.com/server/latest/admin_manual/.
The Docker images we will be using are further documented here: https://github.com/nextcloud/docker
Prerequisites
- A server (e.g. with Hetzner Cloud)
- A S3-compatible bucket (e.g. with Hetzner)
- A domain you want to use for your Nextcloud instance (e.g.
nextcloud.example.com
) ideally with its DNS hosted with Hetzner.
Example terminology
- Server IP:
<server-ip>
- Domain:
nextcloud.example.com
Step 1 - Create Object Storage Bucket
Create a S3-compatible bucket. With Hetzner, see the getting started "Creating a Bucket". Give it a name, preferably a random UUID, like one returned by running uuidgen
. Make sure it is set to private access permissions. We will refer to this bucket as <object-storage-bucket-name>
in the following steps.
Create S3 credentials to access your bucket. With Hetzner, see the getting started "Generating S3 keys". Give the set a descriptive name e.g. nextcloud
. Save the keys in a secure place (e.g. your password manager) as they are not retrievable once the popup is closed.
Further on we will refer to those credentials as <object-storage-access-key>
and <object-storage-secret-key>
.
Step 2 - Create Server
Create a new server. With Hetzner, you can select the Docker CE app as your image, so you don't need to install Docker yourself (see the getting started "Creating a Server"). If you want to install Docker and Docker Compose manually, follow the official Docker documentation after the server was created.
Select an appropriate machine size for your Nextcloud instance preferably from the same location as your storage bucket. Don't select the machine size based on root disk size, we won't be using that for data storage, just RAM and CPU.
Select or add your SSH public key.
Select or create an appropriate firewall. If you use a Hetzner cloud server and you will be following the optional step below of adding a Hetzner Load Balancer, you can deny access from everywhere except SSH from your location.
Step 3 - Deploy Nextcloud
SSH to your server ssh root@<server-ip>
.
Create a directory for your Docker Compose files and folders for the persistent storage of the Nextcloud containers:
mkdir -p /opt/nextcloud/redis
mkdir -p /opt/nextcloud/compose
Step 3.1 - Create deployment and configuration files
vim /opt/nextcloud/compose/compose.yaml
services:
db:
image: mariadb:10.6
restart: always
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
volumes:
- "/opt/nextcloud/mysql:/var/lib/mysql"
environment:
- MYSQL_HOST=db
- MYSQL_ROOT_PASSWORD=${NC_MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=${NC_MYSQL_PASSWORD}
mysqlbackup:
image: selim13/automysqlbackup:2.7.0
restart: always
volumes:
- "/opt/nextcloud/mysql-backups:/backup"
environment:
CRON_SCHEDULE: "0 */4 * * *"
USERNAME: "root"
PASSWORD: "${NC_MYSQL_ROOT_PASSWORD}"
DBHOST: "db"
DBEXCLUDE: "performance_schema information_schema"
EXTRA_OPTS: "--single-transaction"
depends_on:
- db
redis:
image: redis:alpine
restart: always
depends_on:
- app
volumes:
- "/opt/nextcloud/redis:/data"
environment:
- REDIS_HOST=redis
- REDIS_HOST_PORT=6379
entrypoint: redis-server /data/redis.conf
app:
image: nextcloud:30
restart: always
environment:
- NEXTCLOUD_ADMIN_USER=admin
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
- MYSQL_HOST=db
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=${NC_MYSQL_PASSWORD}
- REDIS_HOST=redis
- REDIS_HOST_PORT=6379
- REDIS_HOST_PASSWORD=${REDIS_PASSWORD}
- PHP_MEMORY_LIMIT=2G
- PHP_UPLOAD_LIMIT=15G
- OBJECTSTORE_S3_BUCKET=${OBJECTSTORE_S3_BUCKET}
- OBJECTSTORE_S3_HOST=${OBJECTSTORE_S3_HOST}
- OBJECTSTORE_S3_KEY=${OBJECTSTORE_S3_KEY}
- OBJECTSTORE_S3_SECRET=${OBJECTSTORE_S3_SECRET}
- OBJECTSTORE_S3_SSL=true
- OBJECTSTORE_S3_PORT=443
ports:
- "80:80"
volumes:
- "/opt/nextcloud/html:/var/www/html"
- "/opt/nextcloud/redis/redis-session.ini:/usr/local/etc/php/conf.d/redis-session.ini"
depends_on:
- db
Notes for the compose file above:
- it includes
automysqlbackup
in order to generate regular snapshots of the database. If you use different backup methods, you might want to remove it - it uses the major version tag of the Nextcloud Docker image — 30 at the time of editing. This means it will update to whatever 30.x.x versions will be released but will require you to actively change it to 31, etc. when a next major version lands. This is by design, major version upgrades might need migration steps.
Create an .env
file and add the following variables:
vim /opt/nextcloud/compose/.env
:
COMPOSE_PROJECT_NAME=nextcloud
NC_MYSQL_ROOT_PASSWORD=mysql_securepass
NC_MYSQL_PASSWORD=mysql_pass
REDIS_PASSWORD=redispass
NEXTCLOUD_ADMIN_PASSWORD=ncpass
OBJECTSTORE_S3_HOST=fsn1.your-objectstorage.com
OBJECTSTORE_S3_BUCKET=<object-storage-bucket-name>
OBJECTSTORE_S3_KEY= <object-storage-access-key>
OBJECTSTORE_S3_SECRET=<object-storage-secret-key>
You will of course need to generate stronger passwords than the one in the .env
example above.
Redis configuration files:
vim /opt/nextcloud/redis/redis-session.ini
session.save_handler = redis
session.save_path = "tcp://redis:6379?auth=redispass"
redis.session.locking_enabled = 1
redis.session.lock_retries = -1
redis.session.lock_wait_time = 10000
vim /opt/nextcloud/redis/redis.conf
dir /data
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
requirepass "redispass"
Note: Be sure to use exactly the same password for redis in those two configuration files as in the .env
file above!
Step 3.2 - Start Nextcloud
cd /opt/nextcloud/compose
docker-compose up -d # OR
docker compose up -d # Depending on the way you installed Docker Compose
Edit /opt/nextcloud/html/config/config.php
and add your trusted domains. The trusted_domains
array should be similar to:
'trusted_domains' =>
array (
0 => 'localhost',
1 => '<server-ip>',
2 => 'nextcloud.example.com',
),
In a browser, navigate to http://<server-ip>/
and verify that you can login using admin and the password set for NEXTCLOUD_ADMIN_PASSWORD
in .env
.
Note: If you navigate to
http://something
, modern browsers will immediately redirect tohttps://something
if the response is not immediate. So while waiting for your containers to start, you will need to re-type (paste)http://<server-ip>/
instead of hitting reload.
Navigate to your bucket and doublecheck that files have appeared after you successfully logged in to your Nextcloud instance.
Step 4 - Add a Hetzner Load Balancer (Optional)
If you used a Hetzner cloud server, you can put it behind a Hetzner Load Balancer. To route via a private IP, create a new private Network, e.g. network-nextcloud
, and attach your Nextcloud server to that Network.
-
Select the private Network of the Nextcloud server.
-
For Targets, select Cloud Servers and select the cloud server created in step 2.
-
Under Services select:
- Protocol: https
- Source port: 443
- Destination port: 80
Select + Add certificates. If
example.com
is configured in Hetzner DNS, you can select "Create certificate". Eithernextcloud.example.com
or a wildcard certificate like*.example.com
will do.
Click on Create & Buy now.
Click on your newly created Load Balancer, Services then the edit pen-icon to set the health check advanced settings. Under domain, fill out nextclout.example.com
. This is needed because the healthcheck is running via the private Network and the private IP of your server is not among the trusted domains.
This will only work if example.com
is configured in Hetzner DNS. If not, or you prefer something like caddy or nginx-proxy-manager, you will have to adjust accordingly the way you setup SSL termination.
No matter your method, it is highly recommended to use SSL for your Nextcloud instance.
Conclusion
You are now the owner of a performant and extensible private cloud solution that is completely open source and free from tracking by big corporations.
But with great power comes great responsibility. As mentioned in the introduction, Nextcloud is a big topic so you are advised to consult the official documentation on further steps to improve and secure your instance. Ensuring firewall rules are correct, enabling 2FA for accounts and keeping your installation up-to-date are only the basics of a secure Nextcloud installation.