This is a guide how to host Nextcloud in single node docker swarm on Centos 7 behind HAproxy.
Install and configure iptables
Here is a guide for Basic iptables rules
Add following before COMMIT
-A INPUT -p tcp -m conntrack --ctstate NEW -m multiport --dports 80,443 -m comment --comment "HAproxy" -j ACCEPT
Reload iptables rules
sudo systemctl reload iptables
Bundle certs into one file
HAproxy want all certs bundled into one file. I use Let’s Encrypt certs as an example. Change DOMAIN on following commands.
Generate new dhparam if you don’t have one.
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048 sudo chmod 600 /etc/ssl/certs/dhparam.pem sudo cat DOMAIN.crt DOMAIN.key DOMAIN.ca /etc/ssl/certs/dhparam.pem > /etc/ssl/certs/DOMAIN/ha.bundle sudo chmod 600 /etc/ssl/certs/DOMAIN/ha.bundle sudo restorecon -R /etc/ssl/certs/
You can find Let’s Encrypt Intermediate Certificate ( DOMAIN.ca ) from https://letsencrypt.org/certificates/
Install and configure HAproxy
sudo yum install -y haproxy sudo systemctl enable haproxy sudo cp -av /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.default sudo vi /etc/haproxy/haproxy.cfg
Change DOMAIN
global ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets ssl-default-server-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets user haproxy group haproxy daemon defaults mode http option http-server-close option forwardfor except 127.0.0.0/8 option redispatch retries 3 timeout http-request 10s timeout queue 1m timeout connect 10s timeout client 1m timeout server 1m timeout http-keep-alive 10s timeout check 10s maxconn 30 frontend http bind :80 redirect scheme https code 301 if !{ ssl_fc } frontend public bind :443 ssl crt /etc/ssl/certs/DOMAIN/ha.bundle # Enable HSTS if you know you want it # http-response set-header Strict-Transport-Security max-age=15768000 http-request set-header X-Forwarded-Port %[dst_port] acl host_nextcloud hdr(host) -i DOMAIN use_backend nextcloud if host_nextcloud default_backend deny_backend backend deny_backend http-request deny backend nextcloud option httpchk HEAD / HTTP/1.1\r\nHost:DOMAIN server nextcloud 127.0.0.1:8008 check
Use https://mozilla.github.io/server-side-tls/ssl-config-generator/ to generate up to date configuration.
Be sure you know what HSTS does and then decide if you want to use it.
sudo haproxy -c -f /etc/haproxy/haproxy.cfg sudo systemctl start haproxy
Install docker and docker-compose
sudo yum install -y yum-utils device-mapper-persistent-data lvm2 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
https://docs.docker.com/install/linux/docker-ce/centos/
https://docs.docker.com/compose/install/
Change docker default location (Optional)
sudo vi /etc/docker/daemon.json
{ "data-root":"/NEWLOCATION" }
Start docker and init swarm
sudo systemctl enable docker sudo systemctl start docker sudo docker swarm init
Deploy Nextcloud stack
Create secrets
Use strong passwords.
printf "PASSWORD" | sudo docker secret create POSTGRES_PASSWORD -
https://docs.docker.com/engine/reference/commandline/secret_create/
Create compose file
vi docker-compose.yml
version: '3.7' services: cloud: image: nextcloud:production ports: - 8008:80 volumes: - nextcloud:/var/www/html deploy: replicas: 1 restart_policy: condition: any postgres: image: postgres:11.2 secrets: - POSTGRES_PASSWORD environment: - POSTGRES_PASSWORD_FILE=/run/secrets/POSTGRES_PASSWORD volumes: - postgresql:/var/lib/postgresql/data deploy: replicas: 1 restart_policy: condition: any volumes: nextcloud: postgresql: secrets: POSTGRES_PASSWORD: external: true
https://docs.docker.com/compose/compose-file/
vi deployNextCloudStack.sh
#!/bin/bash docker pull nextcloud:production docker pull postgres:11.2 docker stack deploy --compose-file docker-compose.yml cloud
chmod +x deployNextCloudStack.sh sudo ./deployNextCloudStack.sh
Update Nextcloud and Postgres containers
sudo ./deployNextCloudStack.sh
Nextcloud first time setup
Click Storage & Database open
Select PostgreSQL tab
Database user: postgres
Database password: USERPASSWORD
Database name: nextcloud
Database host: postgres
Database host is just postgres because it is postgreSQL containers name and docker swarm handles internal dns.
Backup with Duplicity and pg_dumpall
Generate PGP key for root user, with strong password
sudo su –
gpg --gen-key # (1) RSA and RSA (default) # 4096 # 2y
Export keys to safe location
pub 4096R/XXXXXXXX
gpg -a --export XXXXXXXX > duplicityGPGpublic.key gpg -a --export-secret-keys XXXXXXXX > duplicityGPGprivate.key
remember to remove .key files from server after you moved them to safe location.
Sign new PGP key with you master PGP key (Optional)
# where you store your master key gpg --import duplicityGPGpublic.key gpg --sign-key --ask-cert-level XXXXXXXX # 3 # y gpg -a --export XXXXXXXX > signedDuplicityGPGpublic.key # move back to server gpg --import signedDuplicityGPGpublic.key
Backup script
I use SFTP to move files and i have added this servers root users SSH publickey to my backup server.
sudo vi backupNextcloud.sh
#!/bin/bash export PASSPHRASE='GPGPASSWORD' duplicity --encrypt-key=XXXXXXXX --sign-key=XXXXXXXX --file-prefix=CLOUDNEXTCLOUD --volsize 1024 --full-if-older-than 1M PATHTO/docker/volumes/cloud_nextcloud/ sftp://user@backupserver/location duplicity remove-all-but-n-full 6 --force sftp://user@backupserver/location docker exec $(docker ps -q -f name=cloud_postgres) /usr/bin/pg_dumpall -cU postgres | gzip | gpg --batch --encrypt --sign --passphrase $PASSPHRASE --output PATHTO/cloud_postgres_backup-$(date +"%m-%d-%Y_%H-%M-%S").sql.gz.gpg -r XXXXXXXX printf "put PATHTO/cloud_postgres_backup-*\nquit" | sftp -b- user@backupserver:/location/ rm -f PATHTO/cloud_postgres_backup-* unset PASSPHRASE
Daily cron
sudo vi /etc/crontab
56 2 * * * root PATHTO/backupNextcloud.sh >> /var/log/backups/nextcloud.log 2>&1
sudo mkdir -p /var/log/backups/
Backup of the backups
My duplicity backups goes to my NAS. NAS will backup itself to AWS Glacier once a month.
Restore
Redo swarm if you lost it too.
- Empty nextcloud and postgres volumes.
- Start stack
- Move postgres backup into postgres volume
-
cp -av cloud_postgres_backup.sql PATHTO/docker/volumes/cloud_postgresql/_data/
- connect to postgres container
-
docker exec -i -t $(docker ps -q -f name=cloud_postgres) /bin/bash
-
su - postgres
-
psql < data/cloud_postgres_backup.sql
-
-
- Stop stack
- Move nextcloud files into nextcloud volume
-
rm -Rf PATHTO/docker/volumes/cloud_nextcloud/_data/*
-
rsync -av nextcloudbackup/* PATHTO/docker/volumes/cloud_nextcloud/_data/
-
chown -R --reference=PATHTO/docker/volumes/cloud_nextcloud/_data/ PATHTO/docker/volumes/cloud_nextcloud/_data/*
-
chcon -R --reference=PATHTO/docker/volumes/cloud_nextcloud/_data/ PATHTO/docker/volumes/cloud_nextcloud/_data/*
-
- Start stack
- Rescan files ( Optional )
-
docker exec --user www-data $(docker ps -q -f name=cloud_cloud) php occ files:scan --all
-
Why did i choose all these components?
Nextcloud
I need some service to sync files across all my and my familys mobile devices. I used to use Mega but now i will try Nextcloud. Even if all data is encrypted in Mega i want to have all data on my own servers.
HAproxy
With HAproxy i can have multiple web service behind one IP. It is also easy to host all web service on same host with HTTP and use HAproxy to handle HTTPS. I don’t need to configure all services to work with HTTPS and i don’t need to give TLS private key to all web services.
Docker swarm
I could have just used docker-compose without swarm. But i wanted practice swarm and use secrets.