Hosting a local Nextcloud
Preface
Setting up your own local Nextcloud can be useful for a number of reasons. The author of this article is interested in using the fantastic file syncing capabilities without putting his data on an server accessible from the public internet. For others what might be of interest is having truly private calendar and contacts that sync between devices, or perhaps using the ‘Note’ application on a private network.
Since we are using Ubuntu Server 20.04 we could get an all-in-one Nextcloud install by using the ‘snap’ from the ‘Snap Store’. The author, however, has used the snap and finds he prefers to make his own choices about how to configure and set up Nextcloud. In addition, since the author has already set up a PostgreSQL server on a Pi as in the PiSQL guide, he wants to use that server for Nextcloud as well. Finally, the redis server that is set up with this guide, will be shared with the Gitea Pi instance he has configured (and it in turn also uses the PiSQL server).
Configure the host server
For that you can follow a guide such as Intel NUC(-like) server or Raspberry Pi OS for a server on at least Pi 2 (older Pi’s a definitely not powerful enough and likely you need at least a Pi 3 for good results). Another option is to use Ubuntu server for Raspberry Pi on at least a Pi 2.
Install OS packages for Nextcloud support
sudo apt update
sudo apt install -y postgresql-client nginx redis php7.4-fpm \
php7.4-gd php7.4-pgsql php7.4-curl php7.4-mbstring php7.4-intl \
php7.4-gmp php7.4-bcmath php-imagick php7.4-xml php7.4-zip php7.4-imap \
php-redis php-imagick ffmpeg libreoffice-core-nogui libreoffice-calc-nogui \
libreoffice-impress-nogui libreoffice-math-nogui libreoffice-draw-nogui \
smbclient
Configure PHP PostgreSQL
Edit /etc/php/7.4/mods-available/pgsql.ini
so it contains the following
settings:
# configuration for php pgsql module
extension=pgsql.so
[PostgresSQL]
pgsql.allow_persistent = On
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1
pgsql.max_links = -1
pgsql.ignore_notice = 0
pgsql.log_notice = 0
And enable the modules for use by PHP-FPM
sudo phpenmod pgsql pdo_psql
Create and install SSL certificates for this server
- Create internal SSL CA and server certificate and key by following the Using
XCA to create private SSL
certificates
article. (As with the PiSQL article, we require server
certificates for the server (e.g.
nextcloud.example.com
); this time we do not need to generate a CA (Certificate Authority) but can use the existing one). - We also need to generate a ‘client’ certificate for this host, which uses the
same procedure, except that instead of selecting the ‘[default] TLS_server’
template, we select the ‘[default] TLS_client’ template. For the client
certificate I recommend using a name such as
nextclouddb
for the CN — you can still add this server’s DNS name(s) as Subject Alternative Names (x509v3 SAN). Note that on the client certificate at one SAN and/or the CN must be the username nextcloud uses to connect to the database.
Assuming you have copied your client nextclouddb.crt and nextclouddb.pem to your admin user, as well as the ca certificate (which we will call ‘root.crt’), and that you are currently in your admin user account:
sudo mkdir /var/www/.postgresql
sudo cp nextclouddb* root.crt /var/www/.postgresql
sudo chown root:www-data /var/www/.postgresql/nextclouddb* /var/www/.postgresql/root.crt
- You can now remove the client cert and key from your admin user account:
rm nextclouddb*
cd /var/www/.postgresql
chmod 750 .
mv nextclouddb.crt postgresql.crt
mv nextclouddb.pem postgresql.key
cp root.crt /usr/local/share/ca-certificates/ca-private.gitea.internal.com
update-ca-certificates
cd ..
NB Keep this session open, we’ll come back to it
On the database server, create user and database for Nextcloud
Login as your admin user to your database server
sudo su - postgres
createuser -P nextclouddb
Enter a new strong password when prompted (you will need to enter it twice).
For C.UTF-8 below substitute the appropriate UTF-8 locale:
createdb -O nextclouddb --encoding UTF8 --locale C.UTF-8 --template template1 nextclouddb
exit
exit
Back on the Nextcloud server, test DB connection
In the session as user www-data
from above and assuming your db server is
named pisql.example.com
:
psql "postgres://nextclouddb@pisql.example.com/nextclouddb?sslmode=verify-full"
Enter the database user’s password when prompted, and you should get a standard ‘psql’ prompt, such as:
psql (11.12 (Raspbian 11.12-0+deb10u1), server 11.11 (Raspbian 11.11-0+deb10u1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
nextclouddb=>
Enter \l
to see the list of databases, and \q
to quit.
Obtain and install Nextcloud
Download Nextcloud and verification information
- Download Nextcloud tarball (e.g.
wget https://download.nextcloud.com/server/releases/nextcloud-21.0.1.tar.bz2
) - Download SHA256 for tarball (e.g.
wget https://download.nextcloud.com/server/releases/nextcloud-21.0.1.tar.bz2.sha256
) - Download GPG signature for tarball (e.g.
wget https://download.nextcloud.com/server/releases/nextcloud-21.0.1.tar.bz2.asc
- Download GPG key (e.g.
wget https://nextcloud.com/nextcloud.asc
)
Verify the downloads
sha256sum --ignore-missing -c nextcloud-21.0.1.tar.bz2.sha256
- Should report ‘OK’
gpg --import nextcloud.asc
gpg --verify nextcloud-21.0.1.tar.bz2.asc nextcloud-21.0.1.tar.bz2
- Should report ‘
Good signature from "Nextcloud Security <security@nextcloud.com>" \[unknown]
’
- Should report ‘
gpg --keyserver keyserver.ubuntu.com --recv-keys D75899B9A724937A
- Should report ‘1 processed, 1 unchanged`
- You no longer require the *.sha256 or *.asc files.
Prepare your installation location
If you plan on storing Nextcloud and/or it’s data on external storage, prepare the storage and mountpoints now.
- Nextcloud needs to be mounted on
/var/www/nextcloud
- The data defaults to being located in
/var/www/nextcloud/data
but that is configurable, and it is recommended that it be moved. You might want to try/srv/nextcloud-data
Extract Nextcloud
sudo tar -C /var/www/ -xvjf nextcloud-21.0.1.tar.bz2
Copy your server certificates in place
sudo mkdir /etc/ssl/nginx
sudo cp nextcloud.example.com.* /etc/ssl/nginx/
sudo chmod 600 /etc/ssl/nginx/nextcloud.example.com.key
Create an Nginx configuration
Copy the following to /etc/nginx/sites-available/nextcloud
and modify as
appropriate.
upstream php-handler {
#server 127.0.0.1:9000;
server unix:/var/run/php/php7.4-fpm.sock;
}
server {
listen 80;
listen [::]:80;
server_name nextcloud.example.com;
# Enforce HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name cloud.example.com;
# Use Mozilla's guidelines for SSL/TLS settings
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
ssl_certificate /etc/ssl/nginx/nextcloud.example.com.crt;
ssl_certificate_key /etc/ssl/nginx/nextcloud.example.com.key;
# HSTS settings
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
# set max upload size
client_max_body_size 512M;
fastcgi_buffers 64 4K;
# Enable gzip but do not remove ETag headers
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;
# Pagespeed is not supported by Nextcloud, so if your server is built
# with the `ngx_pagespeed` module, uncomment this line to disable it.
#pagespeed off;
# HTTP response headers borrowed from Nextcloud `.htaccess`
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
# Path to the root of your installation
root /var/www/nextcloud;
# Specify how to handle directories -- specifying `/index.php$request_uri`
# here as the fallback means that Nginx always exhibits the desired behaviour
# when a client requests a path that corresponds to a directory that exists
# on the server. In particular, if that directory contains an index.php file,
# that file is correctly served; if it doesn't, then the request is passed to
# the front-end controller. This consistent behaviour means that we don't need
# to specify custom rules for certain paths (e.g. images and other assets,
# `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
# `try_files $uri $uri/ /index.php$request_uri`
# always provides the desired behaviour.
index index.php index.html /index.php$request_uri;
# Rule borrowed from `.htaccess` to handle Microsoft DAV clients
location = / {
if ( $http_user_agent ~ ^DavClnt ) {
return 302 /remote.php/webdav/$is_args$args;
}
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Make a regex exception for `/.well-known` so that clients can still
# access it despite the existence of the regex rule
# `location ~ /(\.|autotest|...)` which would otherwise handle requests
# for `/.well-known`.
location ^~ /.well-known {
# The rules in this block are an adaptation of the rules
# in `.htaccess` that concern `/.well-known`.
location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; }
location /.well-known/acme-challenge { try_files $uri $uri/ =404; }
location /.well-known/pki-validation { try_files $uri $uri/ =404; }
# Let Nextcloud's API for `/.well-known` URIs handle all other
# requests by passing them to the front-end controller.
return 301 /index.php$request_uri;
}
# Rules borrowed from `.htaccess` to hide certain paths from clients
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
# Ensure this block, which passes PHP files to the PHP process, is above the blocks
# which handle static assets (as seen below). If this block is not declared first,
# then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
# to the URI, resulting in a HTTP 500 error response.
location ~ \.php(?:$|/) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice
fastcgi_param front_controller_active true; # Enable pretty urls
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}
location ~ \.(?:css|js|svg|gif)$ {
try_files $uri /index.php$request_uri;
expires 6M; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
}
location ~ \.woff2?$ {
try_files $uri /index.php$request_uri;
expires 7d; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
}
# Rule borrowed from `.htaccess`
location /remote {
return 301 /remote.php$request_uri;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
}
Configure Nginx
- Disable default ‘website’
sudo rm /etc/nginx/sites-enabled/default
- Enable Nextcloud configuration
sudo ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/nextcloud
Restart Nginx
systemctl restart nginx
Use the installation wizard (first login)
Or the command line tool (occ). For more details see installation wizard or installing from the command line.
Login
Once you have a successful configuration I recommend a test login. In addition, it would be a good idea to enable “Server-side Encryption” in “Settings”. (Only if you do not have full disk encryption enabled, otherwise the system will get too slow).
Additional configuration
Setup a Fail2Ban filters and jails
In
/etc/fail2ban/filter.d/nextcloud.conf
add:[Definition] _groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*) failregex = ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Login failed: ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Trusted domain error. datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"
In
/etc/fail2ban/jail.d/nextcloud.local
add:[nextcloud] backend = auto enabled = true port = 80,443 protocol = tcp filter = nextcloud maxretry = 3 bantime = 86400 findtime = 43200 logpath = /srv/nextcloud-data/nextcloud.log
Add the following to
/etc/fail2ban/jail.local
[nginx-http-auth] enabled = true [nginx-botsearch] enabled = true # Ban attackers that try to use PHP's URL-fopen() functionality # through GET/POST variables. - Experimental, with more than a year # of usage in production environments. [php-url-fopen] enabled = true
Restart fail2ban:
sudo systemctl restart fail2ban
Verify all is well:
sudo systemctl status -l fail2an
Configure Redis
sudoedit /etc/redis.conf
Uncomment the following lines:
unixsocket /run/redis/redis-server.sock
unixsocketperm 770
sudo adduser www-data redis
(to use the socket, above)
Add:
requirepass a-very-strong-password-obviously-not-what-you-see-here--you-have-been-warned
Configure period tasks (cron)
sudo crontab -u www-data -e
Make sure the following line exists in the crontab
*/5 * * * * php -f /var/www/nextcloud/cron.php
Save and exit.
Tune PHP-FPM
PHP-FPM tuning advice in Nextcloud docs
- Make sure you set
memory_limit
in/etc/php/7.4/fpm/php.ini
to at least512M
. If you do not, you will have issues with sync at least.
Configure default phone number locale
sudoedit /var/www/nextcloud/config/config.php
Add:
'default_phone_region' => 'CA',
in the config ARRAY.
Enjoy your private cloud
Just add users and files and other data…