Nextcloud 13 (NGINX) installation Guide


Following this guide you will be able to install and configure Nextcloud 13 based on Ubuntu 16.04.4 LTS, NGINX 1.14, PHP 7.2, MariaDB, Redis, fail2ban, firewall (ufw) and will achieve an A+ rating from as well Nextcloud as Qualys SSL Labs. We will request and implement the ssl certificate from Let’s Encrypt in chapter 5.

You only have to ammend the red marked values (YOUR.DEDYN.IO, 192.168.2.x, 22, redis-hash) regarding your environment!

If you are interested in running Nextcloud in parralel to other applications like Roundcube, WordPress, Shellinabox, Pi-hole and so on please have a look at Nextcloud 13, Roundcube, WordPress, Shellinabox and Pi-hole behind a NGINX reverse proxy afterwards.


Pre-requirements

From my perspective the requirements for this guide may be rated as low: you only have to

  • provide a 64Bit-SBC (e.g. oDroid C2) or 64Bit “Mini-” Server (e.g. Intel NUC),
  • forward two ports (80 and 443) from internet (your router e.g. FritzBox or Speedport) to your internal Nextcloud server,
  • and install the operating system Ubuntu = 16.04.4 LTS (64Bit).

Table of content

  1. NGINX
  2. PHP
  3. MariaDB
  4. Redis
  5. Nextcloud (SSL enabled, A+)
  6. fail2ban and firewall (ufw)

Updated: April, 17th, 2018:
– updated to nginx 1.14 stable

β†’ earlier changes


Install NGINX

We suggest to upgrade to openssl version 1.1.0h (how to upgrade to openssl 1.1.0h) before starting the entire installation!

sudo -s
apt update && apt upgrade -y && apt install software-properties-common python-software-properties zip unzip screen curl ffmpeg libfile-fcntllock-perl -y
apt remove nginx nginx-common nginx-full -y --allow-change-held-packages
sed -i '$adeb http://nginx.org/packages/ubuntu/ xenial nginx' /etc/apt/sources.list
sed -i '$adeb-src http://nginx.org/packages/ubuntu/ xenial nginx' /etc/apt/sources.list
wget http://nginx.org/keys/nginx_signing.key && apt-key add nginx_signing.key
apt update && apt install nginx -y

Modify NGINX:

systemctl enable nginx.service && apt-mark hold nginx

To update NGINX in the future just issue the following statement:

apt-mark unhold nginx && apt upgrade -y && apt-mark hold nginx

Change NGINX configuration:

mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak && vi /etc/nginx/nginx.conf

to:

user www-data;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
use epoll;
}
http {
server_names_hash_bucket_size 64;
upstream php-handler {
server unix:/run/php/php7.2-fpm.sock;
}
include /etc/nginx/mime.types;
#include /etc/nginx/proxy.conf;
#include /etc/nginx/ssl.conf;
#include /etc/nginx/header.conf;
#include /etc/nginx/optimization.conf;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'"$host" sn="$server_name" '
'rt=$request_time '
'ua="$upstream_addr" us="$upstream_status" '
'ut="$upstream_response_time" ul="$upstream_response_length" '
'cs=$upstream_cache_status' ;
access_log /var/log/nginx/access.log main;
sendfile on;
send_timeout 3600;
tcp_nopush on;
tcp_nodelay on;
open_file_cache max=500 inactive=10m;
open_file_cache_errors on;
keepalive_timeout 65;
reset_timedout_connection on;
server_tokens off;
resolver 192.168.2.1;
# resolver IP is your Router-IP (e.g. your FritzBox)
resolver_timeout 10s;
include /etc/nginx/conf.d/*.conf;
}

Start NGINX:

service nginx restart

Create folders and apply permissions:

mkdir -p /var/nc_data /var/www/letsencrypt /usr/local/tmp/cache /usr/local/tmp/sessions /usr/local/tmp/apc /upload_tmp
chown -R www-data:www-data /upload_tmp /var/nc_data /var/www
chown -R www-data:root /usr/local/tmp/sessions /usr/local/tmp/cache /usr/local/tmp/apc

Install PHP

apt install language-pack-en-base -y && sudo LC_ALL=en_US.UTF-8 add-apt-repository ppa:ondrej/php -y && apt update
apt install php7.2-fpm php7.2-gd php7.2-mysql php7.2-curl php7.2-xml php7.2-zip php7.2-intl php7.2-mbstring php7.2-json php7.2-bz2 php7.2-ldap php-apcu imagemagick php-imagick php-smbclient -y

Awesome, PHP 7.2 is already installed.

In comparison to the previous Nextcloud installation guide you won’t find “php-mcrypt” anymore. It is no longer part of PHP (7.2) and is already discussed here: https://github.com/nextcloud/user_saml/issues/168.

Configure PHP:

cp /etc/php/7.2/fpm/pool.d/www.conf /etc/php/7.2/fpm/pool.d/www.conf.bak
cp /etc/php/7.2/cli/php.ini /etc/php/7.2/cli/php.ini.bak
cp /etc/php/7.2/fpm/php.ini /etc/php/7.2/fpm/php.ini.bak
cp /etc/php/7.2/fpm/php-fpm.conf /etc/php/7.2/fpm/php-fpm.conf.bak
sed -i "s/;env\[HOSTNAME\] = /env[HOSTNAME] = /" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/;env\[TMP\] = /env[TMP] = /" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/;env\[TMPDIR\] = /env[TMPDIR] = /" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/;env\[TEMP\] = /env[TEMP] = /" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/;env\[PATH\] = /env[PATH] = /" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/pm.max_children = .*/pm.max_children = 240/" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/pm.start_servers = .*/pm.start_servers = 20/" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/pm.min_spare_servers = .*/pm.min_spare_servers = 10/" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/pm.max_spare_servers = .*/pm.max_spare_servers = 20/" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/;pm.max_requests = 500/pm.max_requests = 500/" /etc/php/7.2/fpm/pool.d/www.conf
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.2/cli/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 1800/" /etc/php/7.2/cli/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.2/cli/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.2/cli/php.ini
sed -i "s/;upload_tmp_dir =.*/upload_tmp_dir = \/upload_tmp/" /etc/php/7.2/cli/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.2/cli/php.ini
sed -i "s/max_file_uploads =.*/max_file_uploads = 100/" /etc/php/7.2/cli/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.2/cli/php.ini
sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/7.2/cli/php.ini
sed -i "s/;session.save_path =.*/session.save_path = \"N;700;\/usr\/local\/tmp\/sessions\"/" /etc/php/7.2/cli/php.ini
sed -i '$aapc.enable_cli = 1' /etc/php/7.2/cli/php.ini
sed -i "s/memory_limit = 128M/memory_limit = 512M/" /etc/php/7.2/fpm/php.ini
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.2/fpm/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 1800/" /etc/php/7.2/fpm/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.2/fpm/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.2/fpm/php.ini
sed -i "s/;upload_tmp_dir =.*/upload_tmp_dir = \/upload_tmp/" /etc/php/7.2/fpm/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.2/fpm/php.ini
sed -i "s/max_file_uploads =.*/max_file_uploads = 100/" /etc/php/7.2/fpm/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.2/fpm/php.ini
sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/7.2/fpm/php.ini
sed -i "s/;opcache.enable=.*/opcache.enable=1/" /etc/php/7.2/fpm/php.ini
sed -i "s/;opcache.enable_cli=.*/opcache.enable_cli=1/" /etc/php/7.2/fpm/php.ini
sed -i "s/;opcache.memory_consumption=.*/opcache.memory_consumption=128/" /etc/php/7.2/fpm/php.ini
sed -i "s/;opcache.interned_strings_buffer=.*/opcache.interned_strings_buffer=8/" /etc/php/7.2/fpm/php.ini
sed -i "s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=10000/" /etc/php/7.2/fpm/php.ini
sed -i "s/;opcache.revalidate_freq=.*/opcache.revalidate_freq=1/" /etc/php/7.2/fpm/php.ini
sed -i "s/;opcache.save_comments=.*/opcache.save_comments=1/" /etc/php/7.2/fpm/php.ini
sed -i "s/;session.save_path =.*/session.save_path = \"N;700;\/usr\/local\/tmp\/sessions\"/" /etc/php/7.2/fpm/php.ini
sed -i "s/;emergency_restart_threshold =.*/emergency_restart_threshold = 10/" /etc/php/7.2/fpm/php-fpm.conf
sed -i "s/;emergency_restart_interval =.*/emergency_restart_interval = 1m/" /etc/php/7.2/fpm/php-fpm.conf
sed -i "s/;process_control_timeout =.*/process_control_timeout = 10s/" /etc/php/7.2/fpm/php-fpm.conf
sed -i '$aapc.enabled=1' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.file_update_protection=2' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.optimization=0' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.shm_size=256M' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.include_once_override=0' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.shm_segments=1' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.ttl=7200' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.user_ttl=7200' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.gc_ttl=3600' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.num_files_hint=1024' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.enable_cli=0' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.max_file_size=5M' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.cache_by_default=1' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.use_request_time=1' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.slam_defense=0' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.mmap_file_mask=/usr/local/tmp/apc.XXXXXX' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.stat_ctime=0' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.canonicalize=1' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.write_lock=1' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.report_autofilter=0' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.rfc1867=0' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.rfc1867_prefix =upload_' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.rfc1867_name=APC_UPLOAD_PROGRESS' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.rfc1867_freq=0' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.rfc1867_ttl=3600' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.lazy_classes=0' /etc/php/7.2/fpm/php.ini
sed -i '$aapc.lazy_functions=0' /etc/php/7.2/fpm/php.ini
sed -i "s/09,39.*/# &/" /etc/cron.d/php
(crontab -l ; echo "09,39 * * * * /usr/lib/php/sessionclean 2>&1") | crontab -u root -

Modify /etc/fstab

Determine the uid of your www-data user by issuing

id www-data

and only if it differs from ‘uid=33‘ replace the ‘uid=33‘ in the following rows properly before executing them!

sed -i '$atmpfs /tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab
sed -i '$atmpfs /var/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab
sed -i '$atmpfs /usr/local/tmp/apc tmpfs defaults,uid=33,size=300M,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab
sed -i '$atmpfs /usr/local/tmp/cache tmpfs defaults,uid=33,size=300M,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab
sed -i '$atmpfs /usr/local/tmp/sessions tmpfs defaults,uid=33,size=300M,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab

Mount tmpfs and then restart both, PHP and NGINX:

mount -a && service php7.2-fpm restart && service nginx restart

MariaDB

apt update && apt install mariadb-server -y

Secure MariaDB:

mysql_secure_installation

If you already set the db-password for the <root>-User you can skip the first question. All the other following questions should be answered with β€˜Yes’ (Y).

Configure MariaDB:

cp /etc/mysql/my.cnf /etc/mysql/my.cnf.bak && vi /etc/mysql/my.cnf

Change the entire my.cnf-file to:

[server]
skip-name-resolve
innodb_buffer_pool_size = 128M
innodb_buffer_pool_instances = 1
innodb_flush_log_at_trx_commit = 2
innodb_log_buffer_size = 32M
innodb_max_dirty_pages_pct = 90
query_cache_type = 1
query_cache_limit = 2M
query_cache_min_res_unit = 2k
query_cache_size = 64M
tmp_table_size= 64M
max_heap_table_size= 64M
slow-query-log = 1
slow-query-log-file = /var/log/mysql/slow.log
long_query_time = 1

[client-server]
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mariadb.conf.d/

[client]
default-character-set = utf8mb4

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
binlog_format = MIXED
innodb_large_prefix=on
innodb_file_format=barracuda
innodb_file_per_table=1

Restart and connect to MariaDB:

service mysql restart && mysql -uroot

Create the database and the user:

CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER nextcloud@localhost identified by 'nextcloud';
GRANT ALL PRIVILEGES on nextcloud.* to nextcloud@localhost;
FLUSH privileges;
quit;

Redis

apt update && apt install redis-server php-redis -y

Change configuration and group membership:

cp /etc/redis/redis.conf /etc/redis/redis.conf.bak
sed -i "s/port 6379/port 0/" /etc/redis/redis.conf
sed -i s/\#\ unixsocket/\unixsocket/g /etc/redis/redis.conf
sed -i "s/unixsocketperm 700/unixsocketperm 770/" /etc/redis/redis.conf
sed -i "s/# maxclients 10000/maxclients 512/" /etc/redis/redis.conf
usermod -a -G redis www-data

Create a password hash

Issue the following statement (replace the red one to your need)

echo "yourPassWord2BHashed" | sha256sum

and note down the result without the final ‘‘:

d98c51c882960945f49fe8127cb0eb97dbf435b3532bd58c846bd85c2282c4af -

Edit the redis.conf

vi /etc/redis/redis.conf

and paste:

requirepass d98c51c882960945f49fe8127cb0eb97dbf435b3532bd58c846bd85c2282c4af

Modify /etc/sysctl.conf and /etc/rc.local:

cp /etc/sysctl.conf /etc/sysctl.conf.bak && sed -i '$avm.overcommit_memory = 1' /etc/sysctl.conf
cp /etc/rc.local /etc/rc.local.bak && sed -i '$i \sysctl -w net.core.somaxconn=65535' /etc/rc.local

Reboot your server:

shutdown -r now

Create the nextcloud.conf:

sudo -s
mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak && touch /etc/nginx/conf.d/default.conf && vi /etc/nginx/conf.d/nextcloud.conf

Paste the following rows:

server {
server_name YOUR.DEDYN.IO;
#Your DDNS adress, (e.g. from desec.io or no-ip.com) 
listen 80 default_server;
location ^~ /.well-known/acme-challenge {
proxy_pass http://127.0.0.1:81;
proxy_set_header Host $host;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
server_name YOUR.DEDYN.IO;
listen 443 ssl http2 default_server;
root /var/www/nextcloud/;
access_log /var/log/nginx/nextcloud.access.log main;
error_log /var/log/nginx/nextcloud.error.log warn;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
client_max_body_size 10240M;
location / {
rewrite ^ /index.php$uri;
}
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ~ \.(?:flv|mp4|mov|m4a)$ {
mp4;
mp4_buffer_size 100m;
mp4_max_buffer_size 1024m;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
include php_optimization.conf;
fastcgi_pass php-handler;
fastcgi_param HTTPS on;
}
location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
include php_optimization.conf;
fastcgi_pass php-handler;
fastcgi_param HTTPS on;
}
location ~ ^/(?:updater|ocs-provider)(?:$|/) {
try_files $uri/ =404;
index index.php;
}
location ~ \.(?:css|js|woff|svg|gif|png|html|ttf|ico|jpg|jpeg)$ {
try_files $uri /index.php$uri$is_args$args;
access_log off;
expires 30d;
}
}

If you want your Nextcloud running in a subdir like https://your.dedyn.io/nextcloud use this nextcloud.conf instead:

server {
server_name your.dedyn.io;
listen 80 default_server;
location ^~ /.well-known/acme-challenge {
proxy_pass http://127.0.0.1:81;
proxy_set_header Host $host;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
server_name your.dedyn.io;
listen 443 ssl http2 default_server;
root /var/www/;
access_log /var/log/nginx/nextcloud.access.log main;
error_log /var/log/nginx/nextcloud.error.log warn;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /.well-known/carddav {
return 301 $scheme://$host/nextcloud/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/nextcloud/remote.php/dav;
}
client_max_body_size 10240M;
location ^~ /nextcloud {
location /nextcloud {
rewrite ^ /nextcloud/index.php$uri;
}
location ~ ^/nextcloud/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/nextcloud/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ~ \.(?:flv|mp4|mov|m4a)$ {
mp4;
mp4_buffer_size 100m;
mp4_max_buffer_size 1024m;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
include php_optimization.conf;
fastcgi_pass php-handler;
fastcgi_param HTTPS on;
}
location ~ ^/nextcloud/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
include php_optimization.conf;
fastcgi_pass php-handler;
fastcgi_param HTTPS on;
}
location ~ ^/nextcloud/(?:updater|ocs-provider)(?:$|/) {
try_files $uri/ =404;
index index.php;
}
location ~ \.(?:png|html|ttf|ico|jpg|jpeg|css|js|woff|svg|gif)$ {
try_files $uri /nextcloud/index.php$uri$is_args$args;
access_log off;
expires 30d;
}
}
}

Create the letsencrypt.conf:

vi /etc/nginx/conf.d/letsencrypt.conf

Paste the following rows:

server {
server_name 127.0.0.1;
listen 127.0.0.1:81 default_server;
charset utf-8;
access_log /var/log/nginx/le.access.log main;
error_log /var/log/nginx/le.error.log warn;
location ^~ /.well-known/acme-challenge {
default_type text/plain;
root /var/www/letsencrypt;
}
}

Create the ssl.conf:

vi /etc/nginx/ssl.conf

Paste the following rows:

#ssl_certificate /etc/letsencrypt/live/YOUR.DEDYN.IO/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/live/YOUR.DEDYN.IO/privkey.pem;
#ssl_trusted_certificate /etc/letsencrypt/live/YOUR.DEDYN.IO/fullchain.pem;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK:!AES128';
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp384r1;
ssl_stapling on;
ssl_stapling_verify on;

Androider’s: if you run in troubles e.g. using CalDAV/CardDAV please reduce the eliptic curve and cipher strength to:

ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_ecdh_curve prime256v1;

Create the proxy.conf

vi /etc/nginx/proxy.conf

Paste the following rows:

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_connect_timeout 3600;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
proxy_redirect off;

Create the header.conf:

vi /etc/nginx/header.conf

Paste the following rows:

add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "same-origin" always;

Create the optimization.conf:

vi /etc/nginx/optimization.conf

Paste the following rows:

fastcgi_read_timeout 3600;
fastcgi_buffers 64 64K;
fastcgi_buffer_size 256k;
fastcgi_busy_buffers_size 3840K;
fastcgi_cache_key $http_cookie$request_method$host$request_uri;
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
gzip_disable "MSIE [1-6]\.";

Create the php_optimization.conf:

vi /etc/nginx/php_optimization.conf

Paste the following rows:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_cache_valid 404 1m;
fastcgi_cache_valid any 1h;
fastcgi_cache_methods GET HEAD;

Enhance security:

screen -S dhparam
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

To leave screen press STRG+A following by ‘d’ – to resume run screen -r. Please be patient, it will take a while.

Restart NGINX:

sed -i s/\#\include/\include/g /etc/nginx/nginx.conf && service nginx restart

Download and extract Nextcloud:

cd /usr/local/src
wget https://download.nextcloud.com/server/releases/latest.tar.bz2 && tar -xjf latest.tar.bz2 -C /var/www && chown -R www-data:www-data /var/www/ && rm latest.tar.bz2

Install letsencrypt and request your ssl-certificates:

add-apt-repository ppa:certbot/certbot -y && apt update && apt install letsencrypt -y
letsencrypt certonly -a webroot --webroot-path=/var/www/letsencrypt --rsa-key-size 4096 -d YOUR.DEDYN.IO

Apply the permissions using a permissions.sh script:

vi /root/permissions.sh

Paste the following rows:

#!/bin/bash
find /var/www/ -type f -print0 | xargs -0 chmod 0640
find /var/www/ -type d -print0 | xargs -0 chmod 0750
chown -R www-data:www-data /var/www/
chown -R www-data:www-data /upload_tmp/
chown -R www-data:www-data /var/nc_data/
chmod 0644 /var/www/nextcloud/.htaccess
chmod 0644 /var/www/nextcloud/.user.ini
chmod 600 /etc/letsencrypt/live/YOUR.DEDYN.IO/fullchain.pem
chmod 600 /etc/letsencrypt/live/YOUR.DEDYN.IO/privkey.pem
chmod 600 /etc/letsencrypt/live/YOUR.DEDYN.IO/chain.pem
chmod 600 /etc/letsencrypt/live/YOUR.DEDYN.IO/cert.pem
chmod 600 /etc/ssl/certs/dhparam.pem
exit 0

Run the script:

chmod +x /root/permissions.sh && /root/permissions.sh

Modify the ssl.conf and restart NGINX:

sed -i s/\#\ssl/\ssl/g /etc/nginx/ssl.conf && service nginx restart

Create a certificate renewal automatism:

vi /root/renewal.sh

Paste the following rows

#!/bin/bash
cd /etc/letsencrypt
letsencrypt renew
result=$(find /etc/letsencrypt/live/ -type l -mtime -1 )
if [ -n "$result" ]; then
/usr/sbin/service nginx stop
/usr/sbin/service mysql restart
/usr/sbin/service redis-server restart
/usr/sbin/service php7.2-fpm restart
/usr/sbin/service postfix restart
/usr/sbin/service nginx restart
fi
exit 0

Make it exectuable and create a cronjob

chmod +x /root/renewal.sh && crontab -e

Paste the following row:

@monthly /root/renewal.sh 2>&1

Open your browser and call:

https://YOUR.DEDYN.IO

to configure Nextcloud. Enter the following values:

Username: cloudroot
Password*: Your-NC_Password!
Data folder: /var/nc_data
Datenbankuser: nextcloud
DB-Passwort*: nextcloud
Datenbank-Name: nextcloud
Host: localhost

Finish the installation in your browser, and print out the new created values:

egrep "'instanceid' =>.*|'passwordsalt' => '.*|'secret' => '.*" /var/www/nextcloud/config/config.php
'instanceid' => 'ofg69hjknlor0',
'passwordsalt' => 'RrRjXeEeEdddBmJbRnqlnVK7e6R5T3hRX',
'secret' => 'HjKlIz9i8J7G6F5DuGQrqV1L9D8HFj6J8YedSVnTD9d',

Open the config.php as www-data and ammend it:

sudo -u www-data cp /var/www/nextcloud/config/config.php /var/www/nextcloud/config/config.php.bak
sudo -u www-data vi /var/www/nextcloud/config/config.php

Adjust your config.php:

<?php
$CONFIG = array (
 'activity_expire_days' => 14,
 'auth.bruteforce.protection.enabled' => true,
 'blacklisted_files' => 
 array (
 0 => '.htaccess',
 1 => 'Thumbs.db',
 2 => 'thumbs.db',
 ),
 'cron_log' => true,
 'datadirectory' => '/var/nc_data',
 'dbtype' => 'mysql',
 'dbname' => 'nextcloud',
 'dbhost' => 'localhost',
 'dbport' => '',
 'dbtableprefix' => 'oc_',
 'dbuser' => 'nextcloud',
 'dbpassword' => 'nextcloud',
 'enable_previews' => true,
 'enabledPreviewProviders' => 
 array (
 0 => 'OC\\Preview\\PNG',
 1 => 'OC\\Preview\\JPEG',
 2 => 'OC\\Preview\\GIF',
 3 => 'OC\\Preview\\BMP',
 4 => 'OC\\Preview\\XBitmap',
 5 => 'OC\\Preview\\Movie',
 6 => 'OC\\Preview\\PDF',
 7 => 'OC\\Preview\\MP3',
 8 => 'OC\\Preview\\TXT',
 9 => 'OC\\Preview\\MarkDown',
 ),
 'filesystem_check_changes' => 0,
 'filelocking.enabled' => 'true',
 'htaccess.RewriteBase' => '/',
 'installed' => true,
 'instanceid' => '*KeepYourSettings - ofg69hjknlor0*',
 'integrity.check.disabled' => false,
 'knowledgebaseenabled' => false,
 'logfile' => '/var/nc_data/nextcloud.log',
 'loglevel' => 2,
 'logtimezone' => 'Europe/Berlin',
 'log_rotate_size' => 104857600,
 'maintenance' => false,
 'memcache.local' => '\\OC\\Memcache\\APCu',
 'memcache.locking' => '\\OC\\Memcache\\Redis',
 'mysql.utf8mb4' => true,
 'overwriteprotocol' => 'https',
 'overwrite.cli.url' => 'https://your.dedyn.io',
 'passwordsalt' => '*KeepYourSettings: RrRjXeEeEdddBmJbRnqlnVK7e6R5T3hRX*',
 'preview_max_x' => 1024,
 'preview_max_y' => 768,
 'preview_max_scale_factor' => 1,
 'redis' => 
 array (
 'host' => '/var/run/redis/redis.sock',
 'password' => 'd98c51c882960945f49fe8127cb0eb97dbf435b3532bd58c846bd85c2282c4af',
 'port' => 0,
 'timeout' => 0.0,
 ),
 'quota_include_external_storage' => false,
 'secret' => '*KeepYourSettings: HjKlIz9i8J7G6F5DuGQrqV1L9D8HFj6J8YedSVnTD9d*',
 'share_folder' => '/Shares',
 'skeletondirectory' => '',
 'theme' => '',
 'trashbin_retention_obligation' => 'auto, 7',
 'trusted_domains' => 
 array (
 0 => 'your.dedyn.io',
 ),
 'updater.release.channel' => 'stable',
 'version' => '13.0.2.1',
);

Edit the .user.ini:

sudo -u www-data sed -i "s/upload_max_filesize=.*/upload_max_filesize=10240M/" /var/www/nextcloud/.user.ini
sudo -u www-data sed -i "s/post_max_size=.*/post_max_size=10240M/" /var/www/nextcloud/.user.ini
sudo -u www-data sed -i "s/output_buffering=.*/output_buffering='Off'/" /var/www/nextcloud/.user.ini
service php7.2-fpm restart && service nginx restart

If the integrity check within Nextcloud will fail, try to change the config.php

sudo -u www-data vi /var/www/nextcloud/config/config.php

and set :

'integrity.check.disabled' => true,

Then restart all services:

service php7.2-fpm restart && service redis-server restart && service nginx restart

Re-run the integrity check and set the value back to ‘false’:

sudo -u www-data vi /var/www/nextcloud/config/config.php
'integrity.check.disabled' => false,

Restart all services again.

service php7.2-fpm restart && service redis-server restart && service nginx restart

and the message should disappear!


Configure and enable a Nextcloud cron-job:

crontab -u www-data -e

Paste the following row:

*/15 * * * * php -f /var/www/nextcloud/cron.php > /dev/null 2>&1
5 1 * * * php -f /var/www/nextcloud/occ files:scan-app-data > /dev/null 2>&1

Don’t forget to switch from Ajax to Cron in Nextclouds-Adminpanel or use Nextcloud CLI to switch immediately:

sudo -u www-data php /var/www/nextcloud/occ background:cron

Harden your System using fail2ban and ufw

First install and configure fail2ban and finally configure the firewall ufw to secure and harden Nextcloud.

Install and configure fail2ban:

sudo -s
apt update && apt install fail2ban -y

Create the Nextcloud-filter:

vi /etc/fail2ban/filter.d/nextcloud.conf

Paste the following lines:

[Definition]
failregex=^{"reqId":".*","remoteAddr":".*","app":"core","message":"Login failed: '.*' \(Remote IP: '<HOST>'\)","level":2,"time":".*"}$
^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","app":"core".*","message":"Login failed: '.*' \(Remote IP: '<HOST>'\)".*}$
^.*\"remoteAddr\":\"<HOST>\".*Trusted domain error.*$

Create a new jail:

vi /etc/fail2ban/jail.d/nextcloud.local

Paste the following rows:

[nextcloud]
backend = auto
enabled = true
port = 80,443
protocol = tcp
filter = nextcloud
maxretry = 3
bantime = 36000
findtime = 36000
logpath = /var/nc_data/nextcloud.log

Re-start the fail2ban-service:

service fail2ban restart

Configure your ufw (uncomplicated firewall):

ufw allow 80/tcp && ufw allow 443/tcp && ufw allow 22/tcp

Enable and restart ufw by running

ufw enable && service ufw restart

and enjoy your personal data in your secured and hardened Nextcloud-Server!

Don’t forget to Backup your Nextcloud

Find more instructions here: Nextcloud Backup and Restore


Carsten Rieger

135 Responses

  1. Marco says:

    This Step-by-Step-Guide for a PERFECT and highly secure nextcloud-installation is absolutely stunning!
    Thanks so much and a big applause for Carsten for this documentation of his work – you make the world to a lot more better (and safer) place – especialy for our beloved data πŸ˜‰
    You helped me alot in configuring and setup a nextcloud-installation with an A+-Rating in my home-environment. I’m realy happy now πŸ™‚
    Thank you mate..Marco

  2. JC Connell says:

    Carsten, Thanks for the awesome guide. I’ve now followed it a few times while testing different cloud providers. Most recently, I configured it on Vultr’s $2.5/mo VPS plan. It performs well, but the limited RAM (512MB) leaves a little more to be desired. Occasionally the machine will timeout or become very slow.

    I’m looking to free up some RAM and I think the RAMDISK is a good place to start. The VPS is backed by an SSD, so performance should still be good without the RAMDISK. However, I’m not very familiar with PHP and even after reviewing your script, I’m not sure how to completely remove the RAMDISK. Could you give me an idea?

  3. Andrea says:

    Hi Carsten,
    If can be useful, it seems that you must preserve the correct indentation inside the file /etc/fail2ban/filter.d/nextcloud.conf otherwise fail2ban will only consider the first argument. To explain myself better, all the leading ^ must start in line with the first ^ of just after failregex=^

    One last thing, if you can help me, I’ve noticed that my nginx logs (/var/log/nginx/nextcloud.access.log) miss the $remote_user parameter, unless I log in from the android App. If I log via web, even from the LAN, the remote_user gets replaced with a dash (probably because it’s empty). For instance, that’s what I can see:

    192.168.17.55 – – [24/Apr/2018:13:36:41 +0200] “POST /heartbeat HTTP/2.0” 200 690 “-” “Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:59.0)
    however it should say:
    192.168.7.55 – username [24/Apr/2018:13:36:41 +0200] “POST /heartbeat HTTP/2.0” 200 690 “-” “Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:59.0)

    Do you have any idea on why this is the case?

    Thank you!
    Andrea

    • Hi Andrea, many thanks. Could you please post your failban jail? Actually i do not have any ideas regarding the logging, appologizte!

      • Andrea says:

        Hi Carsten, my fail2ban jail is exactly like yours. Were you talking about the file /etc/fail2ban/filter.d/nextcloud.conf ?
        About the logging uissue, I have realized that I do not even need to view the Nginx logs at all, because enabling “admin_audit” and setting ‘loglevel’ => 2, in the config.php file, let me see detailed logs at /var/nc_data/nextcloud.log (that’s also the one used by fail2ban). So, doing like this I have detailed logs about username, source IP address and action on files (create/view/delete) and this is very helpful because I am planning to use Group Folders, and it would be very useful to see if an user misbehaves πŸ™‚
        In the next days I will try to see if I can use these logs to produce some interesting graph (like using Kibana or something) so I can address possible issues in advance on my network.

        Again, thank you so much for this awesome guide, it helped me a lot!
        Andrea

        • Andrea says:

          Err.. I made a typo. It’s required to set –> β€˜loglevel’ => 1 which is info mode. This will also give info about successful logins so if anyone wants to use this setting, expect it to be very verbose!

  4. Robin says:

    When I’m running https://observatory.mozilla.org/analyze
    I get: X-Content-Type-Options -5 X-Content-Type-Options header cannot be recognized

    I can’t understand why, add_header X-Content-Type-Options “nosniff” always; should work right?

    • The header config should look like that:
      add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
      add_header X-Robots-Tag none;
      add_header X-Download-Options noopen;
      add_header X-Permitted-Cross-Domain-Policies none;
      add_header X-Content-Type-Options "nosniff" always;
      add_header X-XSS-Protection "1; mode=block" always;
      add_header Referrer-Policy "same-origin" always;

      and this information should be written to your gateway.conf only
      ...
      server {
      listen 443 ssl http2 default_server;
      server_name your.server.de;
      include /etc/nginx/ssl.conf;
      include /etc/nginx/header.conf;
      ...

      OR

      to the nginx.conf if you don’t configure a gateway.conf (reverse proxy).


      http {
      server_names_hash_bucket_size 64;
      upstream php-handler {
      server unix:/run/php/php7.2-fpm.sock;
      }
      include /etc/nginx/mime.types;
      include /etc/nginx/proxy.conf;
      include /etc/nginx/ssl.conf;
      include /etc/nginx/header.conf;
      include /etc/nginx/optimization.conf;
      ...

      Cheers Carsten

      • Robin says:

        I’ll double check that tonight.
        But if I have my reverse proxy on its own machine I guess that I should exclude this from the nginx. Conf

        upstream php-handler {
        server unix:/run/php/php7.2-fpm.sock;
        }

        • Yes, if you use NGINX as a reverse proxy (either on the same or on different server) following my guides, the header information should be excluded from nginx.conf

          • Robin says:

            And I should also delete this line?
            upstream php-handler {
            server unix:/run/php/php7.2-fpm.sock;
            }

  5. Marek says:

    Hi, can anybody please help me? I recently deployed Nextcloud within this guide and after week i am getting this error:
    Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken

    Any ideas?

    Thanks

    • Please collect all information from your Webserver and Nextcloud logfiles and raise a ticket at Nextcloud forum. What happened on your server, did you apply any updates/changes/new software?

      • Marek says:

        Now, problem is gone. Recently, i have turned on TOTP, but i forgot to create an app password for it, now i did that, so it seems this error was caused by failed authentications with CalDav.

  6. Robin says:

    Hi,
    I did try apt-mark unhold nginx && apt upgrade -y && apt-mark hold nginx to upgrade to NGINX 14 but it don’t work I’m still on nginx v 13

  7. Felix says:

    Hi Carsten,
    i have a question how to get the email funciton from nextcloud work.
    I’m not able to get a proper e-mail connection via SMTP to gmail server, at the moment:
    Connection could not be established with host smtp.gmail.com [ #0]
    Could be, that i have to open some ports to fail2ban and ufw?
    Thanks for your great work and help!

    • You don’t have to open any ports nor modify fail2ban. Please have a look here. Did you set up postfix either?

      • Felix says:

        Hi Carsten, I made my setup like in the description you mentioned.
        I tested with variance of possibilities to connect to smtp.gmail.com and got following error messages:
        first (SSL/TLS on 465): Connection could not be established with host smtp.gmail.com [ #0]
        second (STARTTLS on 587): Unable to connect with TLS encryption
        third (STARTTLS on 465): Expected response code 220 but got code “”, with message “”
        I tried with another email client from strato, and there is no connection possible, too.
        πŸ™
        Do I have to open some port or make a forwarding in my router?

        thanks and regards Felix

        • Rich says:

          I’m also noticing that SMTP isn’t working correctly. I think this has something to do with using the openssl version of 1.1.0 and it not being compatible with the current config of swiftmailer.

          You get these messages in the nextcloud log:

          {“reqId”:”FhNyjn85zFHVOwdb9ZE4″,”level”:3,”time”:”2018-04-25T14:40:04-04:00″,”remoteAddr”:”remote.ip.address.here.ya”,”user”:”user@user.com”,”app”:”PHP”,”method”:”POST”,”url”:”\/settings\/admin\/mailtest”,”message”:”stream_socket_enable_crypto(): SSL operation failed with code 1. OpenSSL Error messages:\nerror:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed at \/var\/www\/nextcloud\/3rdparty\/swiftmailer\/swiftmailer\/lib\/classes\/Swift\/Transport\/StreamBuffer.php#94″,”userAgent”:”Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/66.0.3359.117 Safari\/537.36″,”version”:”13.0.1.1″}

          • Rich says:

            I was able to fix this issue. I’m no expert but here is what I think happened….

            For some reason starting with openssl 1.1.0 the default config directory is /usr/local/ssl NOT /etc/ssl. So when swiftmailer goes to run a test, it fails since there isnt anything in the /usr/local/ssl directory.

            What i needed to do, was edit my /etc/php/7.2/fpm/php.ini file and add this line:

            openssl.cafile=/etc/ssl/certs/ca-certificates.crt

            After adding this line and rebooting my server, email via smtp is working just fine now.

  8. Robin says:

    Hi,
    I’m getting this errors:
    Error PHP unlink(/upload_tmp/oc_tmp_gjZSxU): No such file or directory at /var/www/nextcloud/lib/private/Preview/Movie.php#111

    It happens sometime and the only thing that difference in the lines is the *_gjZ5xU

    It should be nice to fix this issue so I don’t need to re-install the hole thing. πŸ™
    The installation is a clean one.

  9. Andrey says:

    HELP!!!
    Failed authorization procedure. cloud.agt-ua.com (http-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Fetching http://cloud.agt-ua.com/.well-known/acme-challenge/XNn2tS9d7H9-R9LLD1IzNklH1s4-_b2O7VKd11g5Yhk: Connection refused

    • Please ensure having both vhosts configured (nextcloud.conf and letdencrypt. conf) and both ports enabled: 80 and 443.
      Ensure your firewall consists of rules to allow requests from both ports (ufw staus verbose). Last have a look into your router config: did you set the portforwarding properly (80 and 443).

  10. Steve says:

    Thanks for this awesome tutorial! Super helpful and much appreciated! I do have a few questions related to fail2ban and ufw:

    1. Does fail2ban work nicely with ufw out of the box or does it require additional configuration? After I did your test by logging in 3 times with a bad password. I checked the status “fail2ban-client status nextcloud” and I see the banned IP. I then check “ufw status” and I don’t see a rule added to the firewall. I only see my allowed rules. My understanding is that fail2ban should add a rule to the ip table to block the offending IP. In this case, since using ufw, then ufw status should reveal the new rule.

    • Hi Steve, fail2ban creates a rule for banned IPs. You may check it by issuing
      iptables -L -v | grep REJECT && ip6tables -L -v | grep REJECT
      An example:
      iptables -L -v | grep REJECT
      50 4257 REJECT all -- any any tmo-789-012.customers.d2-online.com anywhere reject-with icmp-port-unreachable

      Cheers, Carsten

  11. Mason says:

    Hi Carsten,

    Thank you for taking the time to post these highly detailed nextcloud tutorials. They are super helpful. My nextcloud is up and running and working great. I have an A+ rating, thanks to you. I do have one question though with respect to fail2ban and the log files. I noticed numerous log entries like this: Trusted domain error. “184.105.247.252” tried to access using “xx.xxx.xxx.xx” as host. I do understand the reason for the entry. A significant number of them come from a single IP which tends to change each day. Is there a way to configure fail2ban to automatically ban these offending IP’s? Thank you in advance for your help!

    • Ammend your Nextcloud Filter (vi /etc/fail2ban/filter.d/nextcloud.conf)
      ^.*\"remoteAddr\":\"\".*Trusted domain error.*$

      The entire filter:
      [Definition]
      failregex=^{"reqId":".*","remoteAddr":".*","app":"core","message":"Login failed: '.*' \(Remote IP: ''\)","level":2,"time":".*"}$
      ^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","app":"core".*","message":"Login failed: '.*' \(Remote IP: '
      '\)".*}$
      ^.*\"remoteAddr\":\"
      \".*Trusted domain error.*$

  12. noah williams says:

    Hi great tutorial My question is where is the sync location? how do i change the location? also i am having problems uploading files

    internet_connection_check RedisException: NOAUTH Authentication required.
    /var/www/nextcloud/lib/private/Memcache/Redis.php – line 114: Redis->incrBy(‘ce758c7f62b181b…’, 1)
    /var/www/nextcloud/lib/private/Lock/MemcacheLockingProvider.php – line 73: OC\Memcache\Redis->inc(‘files/135345a3c…’)
    /var/www/nextcloud/lib/private/Files/Storage/Common.php – line 709: OC\Lock\MemcacheLockingProvider->acquireLock(‘files/135345a3c…’, 1)
    /var/www/nextcloud/lib/private/Files/Storage/Wrapper/Wrapper.php – line 592: OC\Files\Storage\Common->acquireLock(‘files_external’, 1, Object(OC\Lock\MemcacheLockingProvider))
    /var/www/nextcloud/lib/private/Files/View.php – line 1926: OC\Files\Storage\Wrapper\Wrapper->acquireLock(‘files_external’, 1, Object(OC\Lock\MemcacheLockingProvider))
    /var/www/nextcloud/lib/private/Files/View.php – line 2036: OC\Files\View->lockPath(‘/files_external’, 1, false)
    /var/www/nextcloud/lib/private/Files/View.php – line 1134: OC\Files\View->lockFile(‘/files_external’, 1)
    /var/www/nextcloud/lib/private/Files/View.php – line 267: OC\Files\View->basicOperation(‘mkdir’, ‘/files_external’, Array)
    /var/www/nextcloud/lib/private/Security/CertificateManager.php – line 123: OC\Files\View->mkdir(‘/files_external…’)
    /var/www/nextcloud/lib/private/Security/CertificateManager.php – line 236: OC\Security\CertificateManager->createCertificateBundle()
    /var/www/nextcloud/lib/private/Http/Client/Client.php – line 74: OC\Security\CertificateManager->getAbsoluteBundlePath(NULL)
    /var/www/nextcloud/lib/private/Http/Client/Client.php – line 137: OC\Http\Client\Client->setDefaultOptions()
    /var/www/nextcloud/settings/Controller/CheckSetupController.php – line 129: OC\Http\Client\Client->get(‘http //www.gith…’)
    /var/www/nextcloud/settings/Controller/CheckSetupController.php – line 112: OC\Settings\Controller\CheckSetupController->isSiteReachable(‘www.github.com’)
    /var/www/nextcloud/settings/Controller/CheckSetupController.php – line 426: OC\Settings\Controller\CheckSetupController->isInternetConnectionWorking()
    [internal function] OC\Settings\Controller\CheckSetupController->check()
    /var/www/nextcloud/lib/private/AppFramework/Http/Dispatcher.php – line 161: call_user_func_array(Array, Array)
    /var/www/nextcloud/lib/private/AppFramework/Http/Dispatcher.php – line 91: OC\AppFramework\Http\Dispatcher->executeController(Object(OC\Settings\Controller\CheckSetupController), ‘check’)
    /var/www/nextcloud/lib/private/AppFramework/App.php – line 115: OC\AppFramework\Http\Dispatcher->dispatch(Object(OC\Settings\Controller\CheckSetupController), ‘check’)
    /var/www/nextcloud/lib/private/AppFramework/Routing/RouteActionHandler.php – line 47: OC\AppFramework\App main(‘OC\\Settings\\Con…’, ‘check’, Object(OC\AppFramework\DependencyInjection\DIContainer), Array)
    [internal function] OC\AppFramework\Routing\RouteActionHandler->__invoke(Array)
    /var/www/nextcloud/lib/private/Route/Router.php – line 297: call_user_func(Object(OC\AppFramework\Routing\RouteActionHandler), Array)
    /var/www/nextcloud/lib/base.php – line 998: OC\Route\Router->match(‘/settings/ajax/…’)
    /var/www/nextcloud/index.php – line 37: OC handleRequest()
    {main}

    • What do you mean by: “My question is where is the sync location? how do i change the location?” Sync using Nextcloud-Client, webdav, cifs, smb…? Following my guide(s) you find all data stored at /var_ncdata/username/files…

      It seems, you have a misconfiguration with redis. If you set a password in /etc/redis/redis.conf you have to configure it in Nextclouds config.php as well. Did you?

      • Noah Williams says:

        Hi Carsten, it was a misconfiguration thank you. and in reply to your message about sync location. I wanted to sync to my external. I see your post on the subject and I will follow.

      • noah williams says:

        yes Carsten was a misconfiguration now i am trying to sync my files to an external that is why i ask the sync location thanks very helpful

  13. Jeff says:

    First – Awesome guide! Thank you for publishing this. One question….without the mcrypt package, you can’t install the SSO & SAML authentication. Is there a workaround or do we just have to wait for a fix?

  14. shelllshock says:

    > echo “yourPassWord2BHashed” | sha256sum
    > and note down the result without the final β€˜β€“β€˜:

    …what about:
    echo “yourPassWord2BHashed” | sha256sum | awk ‘{print $1}’

  15. robbie says:

    Hi Carsten,
    Thanks for such detailed instructions. I tried it and everything worked perfect. Got A+ on both SSL and Nextcloud security scan. Is it possible that you can put this in a script to automate the process? Thanks.

  16. robbie says:

    Hi
    If you install this on virtual cloud provider like digital ocean, will the resolver 192.168.2.1 be equal to the ip of the droplet?

  17. angus says:

    Hi Carsten,
    could you confirm if these files must be created when permmision.sh is launched or before?

    chmod 600 /etc/letsencrypt/live/YOUR.DEDYN.IO/fullchain.pem
    chmod 600 /etc/letsencrypt/live/YOUR.DEDYN.IO/privkey.pem
    chmod 600 /etc/letsencrypt/live/YOUR.DEDYN.IO/chain.pem
    chmod 600 /etc/letsencrypt/live/YOUR.DEDYN.IO/cert.pem

    • Hi Agnus, these files are created automatically while requesting your certificate from e.g. Let’s Encrypt. You don’t have to create these files manually.
      If you would not request a certificate by e.g. Let’s Encrypt you have two options:
      a) use Nextcloud without SSL (remove the statements from permissions.sh) or
      b) create self-signed certificates and replace your mentioned files with e.g.
      /etc/ssl/certs/ssl-cert-snakeoil.pem;
      /etc/ssl/private/ssl-cert-snakeoil.key;

      In case of (b) dont’t forget to substitute these files in your /etc/nginx/ssl.conf as well
      ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
      ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
      ssl_trusted_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;

      Cheers, Carsten

  18. New nextclouder says:

    Hi,

    you have one miss value in config.php at this line = ‘oc’ > you need to change to ‘oc_’, >

    result internal error for me

  19. Felix says:

    Hello Carsten,

    i want to use CalDAV and CarDAV with Nextcloud, but actual i get an error that this service doesn’t run on server.
    i think the configuration in nextcloud.conf with following rows:
    location = /.well-known/carddav {
    return 301 $scheme://$host/remote.php/dav;
    }
    location = /.well-known/caldav {
    return 301 $scheme://$host/remote.php/dav;

    and with the firewall. Could you please help me to set up these?
    thanks and regards

    • Did you enable the calendar and contact app? Really never heared before…

      • Felix says:

        Hi Carsten,
        i have activated both apps, and the task app.
        I can create calenders and contacts in nextcloud without problems. but when i want to use “DAVdroid” with the credentials from “Nextcloud” app i get an error message, that there is no server running.
        My steps in detail:
        -Install and started nextcloud app
        -connect to cloud service (possible).
        -install DAVdroid
        -connect to caldav/carddav via davdroid from nextcloud app
        -get error protocoll in “DAVdroid”
        …[httpClient] <– HTTP FAILED: javax.net.ssl.SSLHandshakeException: Handshake failed …

        • Felix says:

          Hello Carsten,

          i have a complete debug log file from DAVdroid, if this will help i can send it to you.
          Could be, that i have a mistake in some config files or installation of opensll?, but this is hard to find πŸ™

          thanks for your help.

          • As far as you can call Nextclouds Calendar and Contacts app within your Nextcloud server everything in your Nextcloud works properly. So please ask e.g. the davdroid community for further assistance. Appologize, Carsten

          • Felix says:

            Hi Carsten, it looks like it is because of the ssl_ciphers in ssl.conf.
            I found an information, that android needs different cipher suites like
            ssl_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’;
            without “ssl_ecdh_curve secp384r1”

            I will test this.

          • Bei vielen meiner Kunden wird folgendes in der ssl.conf genutzt / many of my customers are using the following ssl.conf:
            ...
            ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
            ssl_prefer_server_ciphers on;
            ssl_ecdh_curve secp384r1;
            ssl_stapling on;
            ssl_stapling_verify on;

            Versuche es mal damit / give it a trial.

          • Felix says:

            Hi Carsten,
            when i comment this part β€œssl_ecdh_curve secp384r1” out, all is going fine for me.

            DAVdroid on Android and NextCloud-Server will communicate very fine after that.

            The Qualy SSL test gives an “A” rating after that.

          • I would suggest to decrease the eliptic curve and cipher strength to:

            ssl_ecdh_curve prime256v1;
            ssl_ciphers ‘ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS’;

            Just to remove shouldn’t be a solution πŸ˜‰

  20. angus says:

    Hi!
    I have a problem with try to launch below script, problem it’s I don’t have created any file /etc/letsrncrypy, do you know whe these file should be created?

    root@angus:/usr/local/src# chmod +x /root/permissions.sh && /root/permissions.sh
    chmod: cannot access ‘/etc/letsencrypt/live/myid.duckdns.org/fullchain.pem’: No such file or directory
    chmod: cannot access ‘/etc/letsencrypt/live/myid.duckdns.org/privkey.pem’: No such file or directory
    chmod: cannot access ‘/etc/letsencrypt/live/myid.duckdns.org/chain.pem’: No such file or directory
    chmod: cannot access ‘/etc/letsencrypt/live/myid.duckdns.org/cert.pem’: No such file or directory
    root@angus:/usr/local/src#

    • Hi, the question is: what was written from Let’sEncrypt, where are the files stored? You can try to find the cert-files:
      cd /
      find ./ -name fullchain.pem

      or re-run the cert-request and have a look to the output from LE. Cheers, Carsten

      • angus says:

        I try to find this file but it was not found.

        root@angus:/# sudo -s
        root@angus:/# cd /
        root@angus:/# find ./ -name fullchain.pem
        find: β€˜./run/user/1000/gvfs’: Permission denied

        After to lauch script again, I can see an authentication error, but I don’t know why, I have not put my password nowhere and duckdns is correctly configured from my server

        root@angus:/# letsencrypt certonly -a webroot –webroot-path=/var/www/letsencrypt –rsa-key-size 4096 -d myid.duckdns.org
        Saving debug log to /var/log/letsencrypt/letsencrypt.log
        Plugins selected: Authenticator webroot, Installer None
        Obtaining a new certificate
        Performing the following challenges:
        http-01 challenge for myid.duckdns.org
        Using the webroot path /var/www/letsencrypt for all unmatched domains.
        Waiting for verification…
        Cleaning up challenges
        Failed authorization procedure. myid.duckdns.org (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://myid.duckdns.org/.well-known/acme-challenge/dJoewWlPjrxlhHblvghqH3rfWkMqraXaeL3ieSs2LIY: ”

        403 Forbidden

        Forbidden
        <p"

        IMPORTANT NOTES:
        – The following errors were reported by the server:

        Domain: myid.duckdns.org
        Type: unauthorized
        Detail: Invalid response from
        http://myid.duckdns.org/.well-known/acme-challenge/dJoewWlPjrxlhHblvghqH3rfWkMqraXaeL3ieSs2LIY:
        "

        403 Forbidden

        Forbidden
        <p"

        To fix these errors, please make sure that your domain name was
        entered correctly and the DNS A/AAAA record(s) for that domain
        contain(s) the right IP address.

        root@angus:/#

        • I tried to reach your server using telnet. Both ports, required for letsencrypt (80/443) are not reachable! As long as your ports are not forwarded to your server properly and not reachable from externals you won’t have success.

          • angus says:

            What address are your using? myid.duckdns.org is not my address.
            I have configured on my router a DMZ to allow all traffic, and also I tried using a NAT path to allow all traffic but my error continue

          • Please ask let’s encrypt for further assistance – it isn’t related to Nextcloud. Appologize, Carsten

    • angus says:

      I think problem is here:

      root@angus:~# letsencrypt certonly -a webroot –webroot-path=/var/www/letsencrypt –rsa-key-size 4096 -d myid.duckdns.org
      Saving debug log to /var/log/letsencrypt/letsencrypt.log
      Plugins selected: Authenticator webroot, Installer None
      Obtaining a new certificate
      Performing the following challenges:
      http-01 challenge for myid.duckdns.org
      Using the webroot path /var/www/letsencrypt for all unmatched domains.
      Waiting for verification…
      Cleaning up challenges
      Failed authorization procedure. myid.duckdns.org
      (http-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Fetching http://myid.duckdns.org/.well-known/acme-challenge/V2y3Y9PfR1FDz9GP_tBdGeB78HWzptP39HuuJ_8iUxk: Timeout

      IMPORTANT NOTES:
      – The following errors were reported by the server:

      Domain: myid.duckdns.org
      Type: connection
      Detail: Fetching
      http://myid.duckdns.org/.well-known/acme-challenge/V2y3Y9PfR1FDz9GP_tBdGeB78HWzptP39HuuJ_8iUxk:
      Timeout

      To fix these errors, please make sure that your domain name was
      entered correctly and the DNS A/AAAA record(s) for that domain
      contain(s) the right IP address. Additionally, please check that
      your computer has a publicly routable IP address and that no
      firewalls are preventing the server from communicating with the
      client. If you’re using the webroot plugin, you should also verify
      that you are serving files from the webroot path you provided.

      I have configured duckdns in my server using this method, because I didn’t want to install php to avoid conflicts with nexcloud

      https://www.duckdns.org/install.jsp

      • “…I have configured duckdns in my server using this method, because I didn’t want to install php to avoid conflicts with nexcloud…” <= I do not get you right. Nextcloud requires PHP essentially...php didn-t cause the issue, your dns record or port forwarding seems to be incorrect.

  21. Simon says:

    Hi, first af all thanks for your manual, it’s great!
    I have a question about the nextcloud.conf configuration phase, when I have to put my ddns name (myddns.no-ip.com) Could I put my domain name if is redirected to my ddns? I mean http://www.simon.com (it’s redirected to simon@no-ip.com).

    Thanks!

    • Hi Simon, that will cause an issue with your SSL certificate, because your domain (SAN) will not match with the domain no-ip.com. But in general, you can configure multiple SNIs in NGINX. Why don’t you redirect directly to your server? Doens’t your provider support a ddclient? Cheers, Carsten

  22. Bruno says:

    Hi again Carsten,

    Thanks a lot for your previous reply.
    Actually I’m having an issue related to ramdisk, the thing is I found in the syslog that the www-data user cannot delete the sessions on filesystem /usr/local/tmp/sessions because it’s a read-only filesystem but I can delete the sessions manually with root, do you have a clue or a workaround for it?:

    I’m receiving this error: Mar 14 11:09:02 nextcloud sessionclean[6913]: find: cannot delete β€˜/usr/local/tmp/sessions/sess_ri3eknl6r6lvmvo46m10reb4vg’: Read-only file system

    Any hint will be appreciated!

    Cheers,
    Bruno.

    • Hi Bruno, please try this and everything should work fine:
      sudo -s
      service php7.2-fpm stop && service nginx stop
      umount -a
      chown -R www-data:root /usr/local/tmp/sessions /usr/local/tmp/cache
      mount -a && service php7.2-fpm restart && service nginx restart

      Cheers, Carsten

      • Bruno says:

        Hi Carsten,

        Thanks for the fast reply!.

        I tried as you suggested but unfortunately is not yet working as it should, after mount and nginx start both cache and sessions directories are changed to root:root

        root@nextcloud:/usr/local/tmp# chown -R www-data:root /usr/local/tmp/sessions /usr/local/tmp/cache
        root@nextcloud:/usr/local/tmp# ls -la
        total 16
        drwxr-xr-x 4 root root 4096 Mar 9 19:37 .
        drwxr-xr-x 12 root root 4096 Mar 9 19:37 ..
        drwxr-xr-x 2 www-data root 4096 Mar 9 19:37 cache
        drwxr-xr-x 2 www-data root 4096 Mar 9 19:37 sessions
        root@nextcloud:/usr/local/tmp# mount -a && service php7.1-fpm restart && service nginx restart
        root@nextcloud:/usr/local/tmp# ls -la
        total 8
        drwxr-xr-x 4 root root 4096 Mar 9 19:37 .
        drwxr-xr-x 12 root root 4096 Mar 9 19:37 ..
        drwxrwxrwt 2 root root 40 Mar 14 11:55 cache
        drwxrwxrwt 2 root root 40 Mar 14 11:55 sessions
        root@nextcloud:/usr/local/tmp#

        Here’s the output of my fstab:

        tmpfs /usr/local/tmp/cache tmpfs defaults,size=250M,noatime,nosuid,nodev,noexec,mode=1777 0 0
        tmpfs /usr/local/tmp/sessions tmpfs defaults,size=250M,noatime,nosuid,nodev,noexec,mode=1777 0 0

        Thanks and cheers,
        Bruno.

        • I could reproduce it and solved it:
          First stop all services (PHP & NGINX) and unmount your devices:
          service php7.2-fpm stop && service nginx stop && umount -a
          regarding your system: service php7.1-fpm stop

          Set the proper directory-permissions:
          chown -R www-data:root /usr/local/tmp/sessions /usr/local/tmp/cache

          Determine the id of your www-data user by issuing
          id www-data
          Note the ID as examplarily “uid=33” and edit your fstab using this information:
          vi /etc/fstab
          Ammend the mounts as follows:
          tmpfs /usr/local/tmp/cache tmpfs defaults,uid=33,size=300M,noatime,nosuid,nodev,noexec,mode=1777 0 0
          tmpfs /usr/local/tmp/sessions tmpfs defaults,uid=33,size=300M,noatime,nosuid,nodev,noexec,mode=1777 0 0

          Mount your devices again
          mount -a
          and validate your permissions using ls -lsa /usr/local/tmp
          Does that fix your issue? …:::Carsten

          • Bruno says:

            Hi Carsten,

            Thanks a lot for the quick reply and the workaround, now it’s working perfect!, the specific UID=33 in the fstab did the trick… now starting all services (php-fpm and nginx) and even rebooting the machine keep the www-data:root ownership.

            Cheers,
            Bruno.

          • Perfect – i already updated the guides for Nextcloud 13

          • UPDATE:
            sed -i “s/;session.save_path =.*/session.save_path = \”N;700;\/usr\/local\/tmp\/sessions\”/” /etc/php/7.2/cli/php.ini
            sed -i “s/;session.save_path =.*/session.save_path = \”N;700;\/usr\/local\/tmp\/sessions\”/” /etc/php/7.2/fpm/php.ini

            “N;700;” changed the phpsession permissions from readonly … so phpsessions can be cleaned now automatically without any read/only issues.

  23. Bruno says:

    Hi Carsten,
    Thanks a lot for your tutorial! everything worked as expected!, the only thing was the letsencrypt certificate… but the day after I tried it worked even though I was doing the same thing… who knows why…
    I would like to ask you regarding the ports 443 and 80, in a previous Nextcloud installation I installed it with apache and I didn’t have to leave the port 80 opened, only the 443, in this case with nginx and letsencrypt should we leave the port 80 always open for the certificate renewal?. Could not be a risk leaving the port 80 opened from security perspective?

    Thanks again and cheers,
    Bruno.

    • Hi Bruno, Great! Yes, the reason is the automatical le-cert-renewal and from my perspective the risk is reduced to a minimum because of the permanent redirect in the vhost “nextcloud.conf”. As long as you won’t use an automatical le-renewal process you can close the port 80 and re-open it only when needed. Cheers, Carsten

  24. Peter says:

    Hi Carsten,
    in config.php you configure mail settings.
    What do I have to configure if I want to use Postfix as described in the Nextcloud 13 advanced guide?

    TIA, Peter

    • You can’t configure “postfix” within Nextcloud. You can either use phpmail (fill in your email) or configure SMTP, similar to the postfix credentials. Cheers Carsten

  25. Henning says:

    Hi Carsten,

    thank you very much for this awesome Nextcloud installation guide! Its the best out there and has a great focus on security.

    I am pretty new to Nginx but have been using Apache2 for a long time. I would like to move the nextcloud webpath to another location (e.g. my domain subdir cloud/) for security reasons:

    ; /etc/nginx/conf.d/nextcloud.conf

    server {
    listen 443 ssl http2 default_server;
    server_name DOMAIN;
    root /var/www/html/; ##serves dummy html file
    […]

    # Nextcloud
    location ^~ /cloud {
    root /var/www/nextcloud;
    rewrite ^ /index.php$uri; ## i am pretty sure this line is faulty – see errorlog below
    }

    location ~ ^/cloud/(?:build|tests|config|lib|3rdparty|templates|data)/ {
    deny all;
    }

    location ~ ^/cloud/(?:\.|autotest|occ|issue|indie|db_|console) {
    deny all;
    }

    location ~ \.(?:flv|mp4|mov|m4a)$ {
    mp4;
    mp4_buffer_size 5m;
    mp4_max_buffer_size 10m;
    fastcgi_split_path_info ^(.+\.php)(/.*)$;
    include fastcgi_params;
    include php_optimization.conf;
    fastcgi_pass php-handler;
    fastcgi_param HTTPS on;
    }

    location ~ ^/cloud/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
    fastcgi_split_path_info ^(.+\.php)(/.*)$;
    include fastcgi_params;
    include php_optimization.conf;
    fastcgi_pass php-handler;
    fastcgi_param HTTPS on;
    }

    location ~ ^/cloud/(?:updater|ocs-provider)(?:$|/) {
    try_files $uri/ =404;
    index index.php;
    }

    }

    My ngix error log does show that the redirect is not working as expected:
    2018/03/09 08:26:04 [error] 13928#13928: *1 open() “/var/www/html/index.php/cloud/index.php” failed (2: No such file or directory), client: MYIP, server: MYDOMAIN, request: “GET /cloud/index.php HTTP/2.0”, host: “MYDOMAIN”

    Could anyone please point me in the right direction? Thank you very much!

  26. carlos says:

    Hi Carsten, I can not answer in the same thread. Thanks for your interest. Theme PHP7.2 & SMB.

    I have done the steps that you have told me and now when I log in, this comes out: “502 Bad Gateway nginx” … I will be looking at nginx.conf …

    • I would like to ask you kindly to set up your server for 80,-€ by myself remotely. The only things you have to prepare are:
      – Ubuntu 16.04 LTS
      – Portforwarding (80/443)
      – SSH (user with sudo’er permissions)
      Are you interested in? Please send me an email. Cheers, Carsten

      • carlos says:

        surely yes. I must consult with the boss. While it is decided, I will try to repeat the installation but with 7.1

        I’ll tell you. thank you very much.

  27. Felix says:

    Hello Carsten,

    Thanks for that How-To!
    I tried your setup and configuration for my own nextcloud server.
    It is very usefull and all worked fine for me.
    I had added a ddclient for direct DNS support and it works.

    But I have made one mistake, by copying the complete config.php from your site in my one.
    Now I get an error message that the user.ini has wrong hash. But i can LogIn via web to the cloud, by another account.
    Could you help me with this error?

    • Hi Felix,
      not sure if i got you right:
      do you mean the a) integrity check that failed or b)did you remove rows 3 to 5 in config.php?
      I gues b)
      'instanceid' => 'abc',
      'passwordsalt' => 'def',
      'secret' => 'ghij',

      New hashes should be generated automatically but passwords are gone…you could create new ones by issuing:
      sudo -u www-data php /var/www/nextcloud/occ user:resetpassword username
      Are you fine with that?
      Cheers, Carsten

      • Felix says:

        Hi Carsten,
        thanks for your fast answer.
        I get a code integritycheck error, when i log in with my administrator user. If i want more informations on that error i get following text displayed

        Technical information
        =====================
        The following list covers which files have failed the integrity check. Please read
        the previous linked documentation to learn more about the errors and how to fix
        them.

        Results
        =======
        – core
        – INVALID_HASH
        – .user.ini

        Raw output
        ==========
        Array
        (
        [core] => Array
        (
        [INVALID_HASH] => Array
        (
        [.user.ini] => Array
        (
        [expected] => ***
        [current] => ***
        )

        )

        )

        )

        I can log in normaly, created a new user, and deleted the old one, but that doesn’t help.

        My first fault was that I has copy this part into my config.php:
        ‘…keep your values…’,
        ‘passwordsalt’ => ‘…keep your values…’,
        ‘secret’ => ‘…keep your values…’,
        ‘trusted_domains’ =>
        .

        • Hi Felix, just add
          ‘integrity.check.disabled’ => true,
          to your config.php.
          sudo -u www-data vi /var/www/nextcloud/config/config.php
          ...
          'integrity.check.disabled' => true,
          }

          Restart all services and issue a rescan within the web-ui.
          Then set the value back to ‘integrity.check.disabled’ => false,
          Restart all services and it should be fixed…fixed it for me and many users in the past. Cheers Carsten

  28. carlos says:

    Thank you very much for this fantastic guide. I followed step by step and now I have a new Nextcloud A +, I repeat, thanks. My problem now is that external storage by SMB does not work. I have activated LDAP and it is ok. The connection to the AD is correct. The SMB server is also in the AD.

    In my old nextcloud 13 it is working ok and I do not see the difference in the configuration.

    Maybe it’s because of the new certificate? In the old nextcloud it is self-signed. Maybe the smb windows server needs something regarding the new letsencrypt certificate?

    • I am happy to hear about your Nextcloud A+! Having regards to SMB, did you install php-smb*? How did you mount the smb share: cifs or nfs? Did you install cifs-utils? Finally, what was written in the logs? Cheers Carsten

      • carlos says:

        In theory, install in the step “install php”. But I realize that it does not appear in “php –modules”. In my old nextcloud yes that is. I have done “apt-get install php-smbclient”, it says all ok, but it does not appear in “modules”.

        Regarding cifs-utils, yes, it is installed. although I do not see where. in “php-modules” it is not.

        The smb shares are mounted as smb: P do not know if cifs or nfs. when mounted I can choose smb or nfs. The “nfs server” is not installed on the windows server.

        the nextcloud log shows this

        Error PHP count(): Parameter must be an array or an object that implements Countable at /var/www/nextcloud/apps/ojsxc/lib/newcontentcontainer.php#36 2018-03-07T16:45:30+0100
        Error PHP count(): Parameter must be an array or an object that implements Countable at /var/www/nextcloud/apps/ojsxc/lib/newcontentcontainer.php#36 2018-03-07T16:45:29+0100
        Error PHP Undefined index: loginForm at /var/www/nextcloud/apps/ojsxc/settings/personal.php#22

        and sorry for my english google πŸ˜‰

        • carlos says:

          ohhh it seems that smbclient is not compatible with php 7.2

          https://github.com/oerdnj/deb.sury.org/issues/815

          How would it affect uninstall 7.2 and install 7.1? I will try while I wait for your opinion;)

          • carlos says:

            I do not know if it would be better to start the installation again, correcting where it says 7.2 put 7.1
            I’m a newbie in Linux, I’m still saying nonsense …

            I’ve read that you can have several versions of php, is it the solution ?, can I now put the 7.1? or should I install them in order?

          • Try to install php-smbclient (v. 7.1) manually
            sudo apt install php-smbclient -y
            If you won’t have success, you can remove PHP 7.2 by issuing
            sudo -s
            apt remove php* --purge && rm -R /etc/php/

            Then install PHP 7.1
            apt install php7.1-fpm php7.1-gd php7.1-mysql php7.1-curl php7.1-xml php7.1-zip php7.1-intl php7.1-mcrypt php7.1-mbstring php-apcu php-imagick php7.1-json php7.1-bz2 php7.1-zip php-smbclient php-redis -y
            and issue php 7.1 mods and
            sed -i "s/port 6379/port 0/" /etc/redis/redis.conf
            sed -i s/\#\ unixsocket/\unixsocket/g /etc/redis/redis.conf
            sed -i "s/unixsocketperm 700/unixsocketperm 770/" /etc/redis/redis.conf
            sed -i "s/# maxclients 10000/maxclients 512/" /etc/redis/redis.conf
            usermod -a -G redis www-data

            That should solve your issue?! Does it?
            Cheers, Carsten

  29. Dan says:

    I can’t get NGINX started. I get the following error:
    root@odroidxu4:/etc/nginx# systemctl status nginx.service
    ● nginx.service – A high performance web server and a reverse proxy server
    Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
    Active: failed (Result: exit-code) since Sun 2018-03-04 23:21:36 UTC; 14s ago
    Process: 3887 ExecStop=/sbin/start-stop-daemon –quiet –stop –retry QUIT/5 –pidfile /run/nginx.pid (code=exited, status=0/SUCCESS)
    Process: 882 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 14572 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=1/FAILURE)
    Main PID: 903 (code=exited, status=0/SUCCESS)

    Mar 04 23:21:36 odroidxu4 systemd[1]: Starting A high performance web server and a reverse proxy server…
    Mar 04 23:21:36 odroidxu4 nginx[14572]: nginx: [emerg] unknown directive “mp4” in /etc/nginx/conf.d/nextcloud.conf:41
    Mar 04 23:21:36 odroidxu4 nginx[14572]: nginx: configuration file /etc/nginx/nginx.conf test failed
    Mar 04 23:21:36 odroidxu4 systemd[1]: nginx.service: Control process exited, code=exited status=1
    Mar 04 23:21:36 odroidxu4 systemd[1]: Failed to start A high performance web server and a reverse proxy server.
    Mar 04 23:21:36 odroidxu4 systemd[1]: nginx.service: Unit entered failed state.
    Mar 04 23:21:36 odroidxu4 systemd[1]: nginx.service: Failed with result ‘exit-code’.

    • Hi Dan, I assume you are running nginx 1.13.9 (nginx -v) – please check /etc/nginx/conf.d/nextcloud.conf around row 41. It seems, you have a misconfiguration around there. It should be:
      location ~ \.(?:flv|mp4|mov|m4a)$ {
      mp4;
      mp4_buffer_size 5m;
      mp4_max_buffer_size 10m;
      fastcgi_split_path_info ^(.+\.php)(/.*)$;
      include fastcgi_params;
      include php_optimization.conf;
      fastcgi_pass php-handler;
      fastcgi_param HTTPS on;
      }

      • Dan says:

        Carsten,

        I think I made a mistake from the very beginning. I did not confirm if my SBC (Odroid HC1) was 64-bit capable, it is not. It is 32-bit. My version of nginx was 1.10.2 and my nextcloud.conf was good. Is there a guide I can use for 32-bit?

        Dan

      • Dan says:

        So I manage to get nginx restart without error, however, I had to comment out some settings in the /etc/nginx/conf.d/nextcloud.conf
        location ~ \.(?:flv|mp4|mov|m4a)$ {
        #mp4;
        #mp4_buffer_size 5m;
        #mp4_max_buffer_size 10m;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        #include php_optimization.conf;
        fastcgi_pass php-handler;
        fastcgi_param HTTPS on;
        }
        location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        #include php_optimization.conf;
        fastcgi_pass php-handler;
        fastcgi_param HTTPS on;
        }

        Everything works until I get to Open your browser and call: Page won’t open.

        • Please remove the entire section”
          location ~ \.(?:flv|mp4|mov|m4a)$ {
          #mp4;
          #mp4_buffer_size 5m;
          #mp4_max_buffer_size 10m;
          fastcgi_split_path_info ^(.+\.php)(/.*)$;
          include fastcgi_params;
          #include php_optimization.conf;
          fastcgi_pass php-handler;
          fastcgi_param HTTPS on;
          }

          What is the result of nginx -t?
          Did you also adjust all the values greater than 2048M (limitation of 32Bit) in PHP and NGINX?

          • Dan says:

            The entire section meaning between the { } along with the { }?

          • everything:
            location ~ \.(?:flv|mp4|mov|m4a)$ {
            #mp4;
            #mp4_buffer_size 5m;
            #mp4_max_buffer_size 10m;
            fastcgi_split_path_info ^(.+\.php)(/.*)$;
            include fastcgi_params;
            #include php_optimization.conf;
            fastcgi_pass php-handler;
            fastcgi_param HTTPS on;
            }

  30. Poison says:

    I have installed everything as described, except fail2ban and ufw.
    In Chrome everything is working good.
    But when connecting as webdav user to “https://xxxxxx.xxx/remote.php/webdav/” it takes 3 minutes to connect, every folder listing takes 1 minute to show and everything is veeeery slow.

    Any idea, what can cause it?

    • I don’t know why.
      Did you have a look in
      a) nextcloud.log
      b) mysql and your Nextcloud-db:
      mysql -u -p use nextcloud;
      select * from oc_bruteforce_attempts;

      Do you find any related information?
      Cheers, Carsten

  31. Robin says:

    Hi again,
    I’m looking at your Fail2Ban filter and I did notice that your watching /var/nc_data/nextcloud.log , I have some more NGINX filters for my fail2ban setup should I redirect them to that log file also?
    Now I have them to /var/log/nginx/*access.log and /var/log/nginx/*error.log

    It’s the following filters:
    nohome
    badbots
    noscript
    noproxy
    http-auth]

  32. Robin says:

    sorry to spam you but sometime when I upload from the client I get the following error, and it seem that this only happens to files bigger then 300MB and not all of the files but some.
    2018-02-28 16:43:20, Original/18.MP4, D:\Home\rstolpe\Videos,Error transferring https://cloud.nohatech.se/remote.php/dav/uploads/rstolpe/3292352839/.file – server replied: Gateway Time-out
    2018-02-28 16:37:57, Original/16.MP4, D:\Home\rstolpe\Videos,Error transferring https://cloud.nohatech.se/remote.php/dav/uploads/rstolpe/1398951712/.file – server replied: Gateway Time-out
    2018-02-28 16:37:40, Original/14.MP4, D:\Home\rstolpe\Videos,Error transferring https://cloud.nohatech.se/remote.php/dav/uploads/rstolpe/1039636236/.file – server replied: Gateway Time-out
    2018-02-28 16:37:10, Original/10.MP4, D:\Home\rstolpe\Videos,Error transferring https://cloud.nohatech.se/remote.php/dav/uploads/rstolpe/2513234162/.file – server replied: Gateway Time-out

    • Hi Robin – no worries.
      Modify and decrease/increase after every success/failure depending to your needs:
      vi /etc/nginx/optimization.conf
      add:
      fastcgi_read_timeout 3600;
      Then restart php and nginx and try again…
      Cheers, Carsten

      • Robin says:

        Hi again,
        Ok, thanks. I’ll try that out tomorrow I don’t have the time now sadly.
        So this not some bug or something this is just because that the file takes to long to upload so I need to adjust the read_timeout?

  33. Robin says:

    Hi,
    Good guide everything works fine, but I have one quastion is it possible to somehow have the Nextcloud system and Ubuntu OS on a SSD and but the Nextcloud storage on a HDD?
    Is it just as simple that I just change the /var/nc_data to my path to the HDD in the webgui installation part of the guide?

    And if I want to turn off HTTP 2 can I just delete that part http2 after the 443 in the nginx config file?

    • Oh yes, as simple as that, but please assign the proper permissions (www-data) to the data directory.
      Why do you want to disable h2? From my perspective really no reasons exists?! Cheers Carsten

      • Robin says:

        I only get around 2-4MB/s in speed with Firefox when I’m using http 2 that’s the reason.

        So just add www-data:www-data to the data folder or should I use my_userbame:www-data?

        • I am aware of known issues regarding http2 and spreed/talk but in general it is recommended for performance reasons (Nextcloud 13 documentation).
          Please open a ticket if this is really related to Nextcloud and http2.

          How to set up another hdd is described at 09.2 Mount an external hdd to your Nextcloud.
          Cheers Carsten

          P.S.: appologize – I forgot to answere regarding http2: yes, just remove the http2 from nextcloud.conf (or gateway.conf) and restart nginx will disable h2 from your server listen 443 ssl http2 default_server;

  34. Poison says:

    Are you from planet Earth? πŸ™‚

    This is the best Nextcloud installation guide.
    You saved weeks of my life.

    Will definitely donate.
    Thank you very much.

  35. Kiko says:

    Hi Carsten!
    Thank You very much for all the tutorials.

    If there is already a WordPress ssl website and I want to add an aditional Nextcloud to the same server, do I need to remove the certificate lines from the ssl.conf file and put them into the site specific .conf file?

    #ssl_certificate /etc/letsencrypt/live/YOUR.DEDYN.IO/fullchain.pem;
    #ssl_certificate_key /etc/letsencrypt/live/YOUR.DEDYN.IO/privkey.pem;
    #ssl_trusted_certificate /etc/letsencrypt/live/YOUR.DEDYN.IO/fullchain.pem;

    Thank You!

  36. Vincent says:

    I think there is a typo the first two lines for the php configuration are the same:
    cp /etc/php/7.2/fpm/pool.d/www.conf /etc/php/7.2/fpm/pool.d/www.conf.bak
    cp /etc/php/7.2/fpm/pool.d/www.conf /etc/php/7.2/fpm/pool.d/www.conf.bak

  37. Reiner says:

    @Frank: Thanks for putting the config together.

    If you don’t want to copy&paste everything, you can find an ansible playbook with the above config here: https://github.com/ReinerNippes/nextcloud13

  38. vincent says:

    Thank you for sharing it’s going to be my tomorrow evening activity !

  1. 12. February 2018

    […] reverse proxy you will find all the neccessary changes below as an ammendment to the initial guide (Nextcloud 13 installation guide). First modify the nginx.conf and remove the header.conf and proxy.conf […]

  2. 15. February 2018
  3. 15. February 2018

    […] Nextcloud 13 installation Guide […]

Leave a Reply

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