Nextcloud SSL - letsencrypt https | Docker
To synchronize contacts, appointments and photos with my NAS, I tested Nextcloud and thus turned my back on other cloud providers for my private data to some extent. Thanks to Docker, the installation is easier and more flexible than ever, allowing NextCloud to run on almost any hardware.
Docker Basics
A container is an isolated environment independent of the operating system (OS):
So the OS does not matter, provided Docker can be installed.
When a container is first launched, Docker independently loads all the necessary sources
aus dem Internet.
Docker can be installed on Windows, macOS or an Linux Distribution
Step by step
Docker and Let's Encrypt reverse proxy:
- Select hardware: PC, notebook or better: virtuel server, Raspberry PI or a NAS: QNAP, Synology or homebrew NAS
- Install OS: Windows, Linux
- Install Docker, see Docker
- create a DNS entry in the internet to the public IP address and
nginx reverse proxy setup: nginx-LetsEncrypt reverse proxy in practice
Container for NextCloud:
- create docker-compose.yml and nginx.conf
- Start container
docker-compose.yml
The docker-compose.yml file contains the config for the containers, such as the database user, the domain and network settings for the communication between the individual containers. The file should be adjusted accordingly before the first start. As mentioned above, the setup described here requires a DNS entry on the IP of the Nextcloud installation in addition to Docker and the nginx-LetsEncrypt reverse proxy.
Content of the file: docker-compose.yml
version: '2'
services:
web:
image: nginx
container_name: nextcloud_webserver
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
links:
- app
volumes_from:
- app
environment:
- VIRTUAL_HOST=next.domain.tld
- VIRTUAL_NETWORK=nginx-proxy
- VIRTUAL_PORT=80
- LETSENCRYPT_HOST=next.domain.tld
- LETSENCRYPT_EMAIL=admin@domain.tld
networks:
- nextcloud
restart: always
app:
image: nextcloud:fpm
container_name: nextcloud_fpm
links:
- db
- redis
volumes:
- ./apps:/var/www/html/apps
- ./config:/var/www/html/config
- ./data:/var/www/html/data
networks:
- nextcloud
restart: always
redis:
image: redis
container_name: nextcloud_redis
restart: always
ports:
- "6379:6379"
volumes:
- ./redis:/data
entrypoint: redis-server --appendonly yes
networks:
- nextcloud
restart: always
db:
image: mariadb:10.5
container_name: nextcloud_db
volumes:
- ./db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=xxx
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=xxx
networks:
- nextcloud
restart: always
networks:
nextcloud:
external:
name: webproxy
nginx.conf
Contents of nginx.conf file
# config file for nginx webserver to work with NextCloud app.
user www-data;
events {
worker_connections 2048;
}
http {
upstream backend {
server app:9000;
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
# Add headers to serve security related headers
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
root /var/www/html;
client_max_body_size 0; # 0=unlimited - set max upload size
fastcgi_buffers 64 4K;
gzip off;
index index.php;
error_page 403 /core/templates/403.php;
error_page 404 /core/templates/404.php;
rewrite ^/.well-known/carddav /remote.php/dav/ permanent;
rewrite ^/.well-known/caldav /remote.php/dav/ permanent;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ~ ^/(build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location / {
rewrite ^/remote/(.*) /remote.php last;
rewrite ^(/core/doc/[^\/]+/)$ $1/index.html;
try_files $uri $uri/ =404;
}
location ~ \.php(?:$|/) {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true; #Avoid sending the security headers twice
fastcgi_pass backend;
fastcgi_intercept_errors on;
}
# Adding the cache control header for js and css files
# Make sure it is BELOW the location ~ \.php(?:$|/) { block
location ~* \.(?:css|js)$ {
add_header Cache-Control "public, max-age=7200";
# Add headers to serve security related headers
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
# Optional: Don't log access to assets
access_log off;
}
# Optional: Don't log access to other assets
location ~* \.(?:jpg|jpeg|gif|bmp|ico|png|swf)$ {
access_log off;
}
}
}
Creating the config and starting the containers
The following command creates the containers with the config stored in docker-compose.yml.
docker-compose up -d
Add container / customize docker-compose.yml
Add Collabora
Collabora is an online version of LibreOffice and allows you to create or collaborate on Office documents online via your web browser. Prerequisite for use from the Internet is an additional DNS name, i.e. an A-record to the public IP of the server and an additional Let`s Encrypt certificate, which in turn can be defined via the environment variables and is automatically issued and extended by the nginx-LetsEncrypt reverse proxy.
To use Callabora in Nextcloud, the following block can be customized and inserted in docker-compose.yml:
collabora:
image: collabora/code
container_name: nextcloud_collabora
expose:
- 9980
cap_add:
- MKNOD
environment:
- domain=next.deineDomain
- VIRTUAL_HOST=office.deineDomain
- VIRTUAL_NETWORK=nginx-proxy
- VIRTUAL_PORT=9980
- VIRTUAL_PROTO=https
- LETSENCRYPT_HOST=office.deineDomain
- LETSENCRYPT_EMAIL=deineEmail-Addresse
networks:
- nextcloud
restart: always
Add OnlyOffice
Only-Office can also be used to edit Word, Excel or PowerPoint documents online. Here, too, an additional DNS name is required for access from the Internet : nginx-LetsEncrypt Reverse Proxy. Compared to Collabora, Only-Office is a bit more streamlined, since much more program logic takes place in the browser. Compared to Collabora, I have not had any disconnections when editing documents with OnlyOffice, so I replaced Collabora with OnlyOffice in the meantime.
onlyoffice:
image: onlyoffice/documentserver
container_name: nextcloud_onlyoffice
stdin_open: true
tty: true
volumes:
- ./onlyoffice/data:/var/lib/onlyoffice/documentserver/App_Data
expose:
- '80'
- '443'
environment:
- VIRTUAL_HOST=document.domain.xx
- VIRTUAL_NETWORK=nginx-proxy
- LETSENCRYPT_HOST=document.domain.xx
- LETSENCRYPT_EMAIL=email@domain.xx
- JWT_ENABLED=true
- JWT_SECRET=secretwithoutnumbers
- JWT_HEADER=Authorization
networks:
- nextcloud
restart: always
Customized docker-compose.yml with OnlyOffice
version: '2'
services:
web:
image: nginx
container_name: nextcloud_webserver
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
links:
- app
volumes_from:
- app
environment:
- VIRTUAL_HOST=next.domain.tld
- VIRTUAL_NETWORK=nginx-proxy
- VIRTUAL_PORT=80
- LETSENCRYPT_HOST=next.domain.tld
- LETSENCRYPT_EMAIL=admin@.domain.tld
networks:
- nextcloud
restart: always
app:
image: nextcloud:fpm
container_name: nextcloud_fpm
links:
- db
- redis
- onlyoffice
volumes:
- ./apps:/var/www/html/apps
- ./config:/var/www/html/config
- ./data:/var/www/html/data
networks:
- nextcloud
restart: always
redis:
image: redis
container_name: nextcloud_redis
restart: always
ports:
- "6379:6379"
volumes:
- ./redis:/data
entrypoint: redis-server --appendonly yes
networks:
- nextcloud
restart: always
onlyoffice:
image: onlyoffice/documentserver
container_name: nextcloud_onlyoffice
stdin_open: true
tty: true
volumes:
- ./onlyoffice/data:/var/lib/onlyoffice/documentserver/App_Data
expose:
- '80'
- '443'
environment:
- VIRTUAL_HOST=document.domain.tld
- VIRTUAL_NETWORK=nginx-proxy
- LETSENCRYPT_HOST=document.domain.tld
- LETSENCRYPT_EMAIL=adin@domain.tld
- JWT_ENABLED=true
- JWT_SECRET=xxx
- JWT_HEADER=Authorization
networks:
- nextcloud
restart: always
db:
image: mariadb:10.5
container_name: nextcloud_db
volumes:
- ./db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=xxx
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=xxx
networks:
- nextcloud
restart: always
networks:
nextcloud:
external:
name: webproxy
OnlyOffice empty documents
ATTENTION: If the address for the internal requests cannot be resolved, the documents will not be updated in NextCloud, they will still be in the container, in the folder: /var/lib/onlyoffice/documentserver/App_Data. For this reason, the folder should definitely be mapped to a local folder to avoid data loss. Responsible for updating the Only-Office documents in NextCloud is the cron job, so it is also not insignificant that it runs regularly.
I also had to add the following lines in the Nextcloud config starting from a certain version:
File: /config/config.php
$CONFIG = array (
...
'onlyoffice' =>
array (
'verify_peer_off' => false,
),
...
Maintenance
Update container
For the update of the container I wrote myself a small .sh file:
docker-compose pull
docker-compose up -d
sleep 20
docker exec --user www-data nextcloud_fpm php occ upgrade
sleep 20
docker exec --user www-data nextcloud_fpm php occ db:add-missing-indices
docker exec --user www-data nextcloud_fpm php occ app:disable twofactor_totp calendar news contacts onlyoffice previewgenerator tasks mail drawio
docker exec --user www-data nextcloud_fpm php occ app:enable twofactor_totp calendar news contacts onlyoffice previewgenerator tasks mail drawio
docker exec --user www-data nextcloud_fpm php occ maintenance:mode --off
~
After apps were missing from time to time, I first deactivated them in the script and then reactivated them.
Without the "occ upgrade" command, the upgrade would be initiated the next time the web interface was called via the browser:
As used in the Bash-Script: Various apps may have to be reactivated, either via the GUI or via command:
docker exec --user www-data nextcloud_fpm php occ app:enable twofactor_totp calendar news contacts onlyoffice phonetrack previewgenerator spreed audioplayer
scan files
docker exec --user www-data nextcloud_fpm php occ files:scan --all
rebuild birthday calendar
docker exec --user www-data nextcloud_fpm php occ dav:sync-birthday-calendar User
backup: database dump
docker exec db mysqldump --user=root --password=password -h localhost nextcloud > dump.sql.gz
Tuning - Settings - Optimizations
CronJob
on the host OS
sudo crontab -e
*/5 * * * docker exec --user www-data nextcloud_fpm php -f /var/www/html/cron.php > /dev/null 2>&1
Security and setup warnings
docker exec --user www-data nextcloud_fpm php occ maintenance:mode --on
docker exec --user www-data nextcloud_fpm php occ db:convert-filecache-bigint
docker exec --user www-data nextcloud_fpm php occ maintenance:mode --off
docker exec --user www-data nextcloud_fpm php occ db:add-missing-indices
Brute Force Protection and Reverse Proxy:
In logging:
/config/config.php:
add from:
'trusted_proxies' => array('172.18.0.2'),
'forwarded_for_headers' => array('HTTP_X_FORWARDED_FOR')
Connections:
root@soxn:/nextcloud# nano nginx.conf
events { worker_connections 768; }
changed to 2048;
NGINX Bad Gateway - OnlyOffice
Initially, I needed the following workaround to start OnlyOffice, but it has since become obsolete ...
Bash script:
#!/bin/bash
url="https://document.DOMAIN"
keyword="Bad Gateway"
if curl -s "$url" | grep "$keyword"
then
# if the keyword is in the conent
echo " ERROR"
docker restart onlyoffice
else
echo "the website is working fine"
fi
sudo crontab -e
*/10 * * * * . /nextcloud/check.sh > /dev/null 2>&1
Preview Images
Show the last changed files:
find . -type f -cmin 5
Read setting: docker exec --user www-data nextcloud_fpm php -d memory_limit=4G occ config:app:get previewgenerator squareSizes
docker exec --user www-data nextcloud_fpm php -d memory_limit=4G occ config:app:set --value "256" previewgenerator squareSizes
client_max_body_size
File uploadsize.conf
client_max_body_size 10G;
docker-compose.yml
services:
proxy:
image: jwilder/nginx-proxy
container_name: proxy
ports:
- 80:80
- 443:443
volumes:
- ./proxy/conf.d:/etc/nginx/conf.d
- ./proxy/vhost.d:/etc/nginx/vhost.d
- ./proxy/html:/usr/share/nginx/html
- ./proxy/certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./uploadsize.conf:/etc/nginx/conf.d/uploadsize.conf:ro
Redis
config/apcu.config.php
<?php
$CONFIG = array (
'memcache.local' => '\OC\Memcache\APCu',
'redis' => array(
'host' => 'nextcloud_redis',
'port' => 6379,
),
'memcache.locking' => '\OC\Memcache\Redis',
);
Docker .env
If a .env file is created in the folder where the docker-compose.yml file is located, it can be used to modify Docker parameters:
#.env
COMPOSE_HTTP_TIMEOUT=200
Troubleshooting
Internal Server Error
After a power failure I had the following problem:
Internal Server Error
The server encountered an internal error and was unable to complete your request.
Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.
More details can be found in the server log.
The Redis container kept restarting. In the log I could find the following entry:
Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix
After I deleted the redis folder ./nextcloud/redis the installation could be started again.
WARNING:[pool www] server reached pm.max_children setting (5), consider raising it
With the default settings, the following warning was displayed in the log of the nextcloud:fpm container:
WARNING: [pool www] server reached pm.max_children setting (5), consider raising it
The limit can be raised in the FPM config file www.conf, which is hidden in the nextcloud:fpm container:
To view the file, we can connect to the container:
docker exec --user www-data -it nextcloud_fpm bash
To make the changes to the config file available after a reboot, the file can be placed outside the container, but to do this the file must exist:
Use cat /usr/local/etc/php-fpm.d/www.conf to display the contents of www. conf and create www.conf in the root folder:
Add the following line to nextcloup_fpm:
- ./www.conf:/usr/local/etc/php-fpm.d/www.conf #added 4 max-childs-config
with nano.conf the www. conf can be adjusted:
After I have 8GB Ram in my NAS I adjusted the following values:
pm.max_children = 80 #(vorher 5)
pm.start_servers = 2
pm.min_spare_servers = 2 #(voher 1)
pm.max_spare_servers = 15 #(vorher 3)
The complete file looks like this for me:
www.conf
[www]
user = www-data
group = www-data
listen = 127.0.0.1:9000
pm = dynamic
pm.max_children = 80
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 15
Exception: Database error when running migration latest for app core
An exception occurred while executing a query: SQLSTATE[HY000]: General error: 4047 InnoDB refuses to write tables with ROW_FORMAT=COMPRESSED or KEY_BLOCK_SIZE.
The problem is due to the MariaDB version. From version 10.6 on compressed columns are not supported anymore.
I have solved the problem for now by using version 10.5 of MariaDB:
docker-compose.yml
...
db:
image: mariadb:10.5
container_name: nextcloud_db
...
see: help.nextcloud.com/t/update-to-22-failed-with-database-error-updated/120682
Docker as user / without root privileges
sudo groupadd docker
sudo gpasswd -a $USER docker
Router OpenWrt- internal IP
Since my NAS and thus the NextCloud installation at home is directly accessible via WLAN, without detour: WLAN - router - Internet - router, it makes sense of course to call the installation directly, for this an additional DNS entry at the router is sufficient: Split-DNS. The NextCloud domain is thus resolved in the WLAN with an internal IP.
Here is an example of how to configure my OpenWrt-based router:
vi /etc/config/dhcp
config 'domain'
option name 'next.domain.xx'
option ip 192.168.1.5
config 'domain'
option name 'office.domain.xx'
option ip 192.168.1.5
config 'domain'
option name 'document.domain.xx'
option ip 192.168.1.5
Windows webdav
\next.domain.xx@SSL\DavWWWRoot\remote.php\dav\files\user
FolderSync Rasperry PI
Install:
sudo apt install owncloud-client-cmd
and add it to crontab
crontab -e
* * * owncloudcmd /localfolder https://user:password@nextcloudURL/remote.php/webdav/Ordner >/dev/null 2>&1
Conclusion
NextCloud adds countless features to my NAS and gives me the ability to bring cloud services back home:
With NextCloud I now have sync clients for Android, Windows and Linux, can create office documents, charts or polls in the browser. Besides the photos from the SmartPhone, I can also synchronize or share my contacts and calendar entries and could even run my own messenger incl. call and video function and much more ...

{{percentage}} % positive
