Install Nextcloud on Raspberry Pi with NGINX
Install and Configure PHP 8.0
As of this writing, the Raspbian 10 repository allows installing PHP 7.3. We want 8.0 instead so let’s add the source to Ondřej Surý‘s PHP packages:
sudo wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
echo "deb https://packages.sury.org/php/ buster main" | sudo tee /etc/apt/sources.list.d/php.list
sudo apt update
Now we can install the latest available PHP 8.0 and the necessary modules for Nextcloud:
sudo apt install php8.0-fpm php8.0-curl php8.0-cli php8.0-mysql php8.0-gd php8.0-common php8.0-xml php8.0-json php8.0-intl php8.0-imagick php8.0-dev php8.0-mbstring php8.0-zip php8.0-soap php8.0-bz2 php8.0-bcmath php8.0-gmp php8.0-imap php8.0-opcache php8.0-apcu php8.0-redis -y
In the main php.ini file, uncomment the date.timezone
line by removing the preceding ;
and change the value with your own. Uncomment also the cgi.fix_pathinfo
line and change the value to 0
.
sudo nano /etc/php/8.0/fpm/php.ini
798c798
< ;cgi.fix_pathinfo = 1 --- > cgi.fix_pathinfo=0
962c962
< ;date.timezone = America/Denver --- > date.timezone = Europe/London
Save and exit (CTRL+X, press Y to confirm and then Enter).
Do the same in the CLI’s php.ini:
sudo nano /etc/php/8.0/cli/php.ini
Next, let’s add a pool configuration file:
sudo nano /etc/php/8.0/fpm/pool.d/nextcloud.conf
[nextcloud]
user = www-data
group = www-data
listen.owner = www-data
listen.group = www-data
listen = /run/php/nextcloud.sock
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/sessions
php_value[max_execution_time] = 3600
php_value[memory_limit] = 7G
php_value[post_max_size] = 7G
php_value[upload_max_filesize] = 7G
php_value[max_input_time] = 3600
php_value[max_input_vars] = 2000
php_value[date.timezone] = Europe/London
;php_value[opcache.enable] = 1
;php_value[opcache.enable_cli]=1
php_value[opcache.memory_consumption] = 128
php_value[opcache.interned_strings_buffer] = 8
php_value[opcache.max_accelerated_files] = 10000
php_value[opcache.revalidate_freq] = 1
php_value[opcache.save_comments] = 1
Restart PHP and set the service to start automatically after a server reboot:
sudo systemctl restart php8.0-fpm && sudo systemctl enable php8.0-fpm
Install and Configure MariaDB Server
In this step, we will install the latest MariaDB version and create a new database for the Nextcloud installation. The latest MariaDB packages are available on the repository by default so let’s install using the command below:
sudo apt install mariadb-server -y
After the installation is complete, enable the service to launch every time the system reboots:
sudo systemctl enable mariadb
Next, we will configure the MariaDB root password using the mysql_secure_installation
shell script:
sudo mysql_secure_installation
Press Enter and set a password for the root user. Type Y
for the subsequent questions unless you want to do differently:
Enter current password for root (enter for none):
Set root password? [Y/n]
Remove anonymous users? [Y/n]
Disallow root login remotely? [Y/n]
Remove test database and access to it? [Y/n]
Reload privilege tables now? [Y/n]
Create a User and Database on MariaDB for NextCloud
sudo mysql
CREATE DATABASE nextcloud;
GRANT ALL PRIVILEGES ON nextcloud.* TO nextclouduser@'localhost' IDENTIFIED BY 'CHANGEwithYOURpassword';
FLUSH PRIVILEGES;
EXIT
Let’s Encrypt certificate via Cloudflare DNS
I prefer DNS-01 challenge over HTTP-01 and as I’m also behind Cloudflare, I’m going to use the certbot-dns-cloudflare plugin on top of the certbot instructions for NGINX with snapd to generate an SSL certificate.
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflare
Create a Cloudflare API Token with an Edit zone DNS template and choose to include the desired specific zone. E.g.:
Create a /root/.cloudflare.ini file and add your token in it. E.g.:
# Cloudflare API token used by Certbot
dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567
Restrict permissions:
sudo chmod 600 /root/.cloudflare.ini
Issue the SSL certificate with the command below and enter an email address to be informed if the certificate doesn’t renew automatically (i.e. there’s an issue) and answer the questions:
sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials /root/.cloudflare.ini -d nextcloud.oviliz.com --post-hook 'service nginx restart'
Do a --dry-run
renewal to verify that certbot remembers now to use the DNS challenge:
sudo certbot renew --dry-run
Install Nextcloud
sudo wget https://download.nextcloud.com/server/releases/latest.zip -P /var/www/
sudo unzip /var/www/latest.zip -d /var/www/
sudo chown -R www-data:www-data /var/www/nextcloud
sudo nano /etc/nginx/sites-available/nextcloud.oviliz.com
upstream php-handler {
#server 127.0.0.1:9000;
server unix:/run/php/nextcloud.sock;
}
server {
listen 80;
listen [::]:80;
server_name nextcloud.oviliz.com;
# enforce https
return 301 https://$server_name:443$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name nextcloud.oviliz.com;
# Use Mozilla's guidelines for SSL/TLS settings
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
# NOTE: some settings below might be redundant
ssl_certificate /etc/letsencrypt/live/nextcloud.oviliz.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nextcloud.oviliz.com/privkey.pem;
# Add headers to serve security related headers
# Before enabling Strict-Transport-Security headers please read into this
# topic first.
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
#
# 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 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;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# The following 2 rules are only needed for the user_webfinger app.
# Uncomment it if you're planning to use this app.
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
# The following rule is only needed for the Social app.
# Uncomment it if you're planning to use this app.
#rewrite ^/.well-known/webfinger /public.php?service=webfinger last;
location = /.well-known/carddav {
return 301 $scheme://$host:$server_port/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host:$server_port/remote.php/dav;
}
# set max upload size
client_max_body_size 7G;
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;
# Uncomment if your server is build with the ngx_pagespeed module
# This module is currently not supported.
#pagespeed off;
location / {
rewrite ^ /index.php;
}
location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
deny all;
}
location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy|.+\/richdocumentscode_arm64\/proxy|)\.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;
# Avoid sending the security headers twice
fastcgi_param modHeadersAvailable true;
# Enable pretty urls
fastcgi_param front_controller_active true;
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
# Raise timeout values.
# This is especially important when the Nextcloud setup runs into timeouts (504 gateway errors)
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
fastcgi_connect_timeout 600;
}
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}
# Adding the cache control header for js, css and map files
# Make sure it is BELOW the PHP block
location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
try_files $uri /index.php$request_uri;
add_header Cache-Control "public, max-age=15778463";
# Add headers to serve security related headers (It is intended to
# have those duplicated to the ones above)
# Before enabling Strict-Transport-Security headers please read into
# this topic first.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
#
# 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 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;
# Optional: Don't log access to assets
access_log off;
}
location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$ {
try_files $uri /index.php$request_uri;
# Optional: Don't log access to other assets
access_log off;
}
}
sudo ln -s /etc/nginx/sites-available/nextcloud.oviliz.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
Now, access the URL [https://(chosenDomain)/] with the web browser and configure Administrative user account as well as the database connection details as previously created. Filled the admin user and database information, complete the installation and wait until is done.
Install Redis
sudo apt install redis-server
sudo nano /var/www/nextcloud/config/config.php
Configure Nextcloud to use APCu and Redis memcaches:
'memcache.local' => '\OC\Memcache\APCu',
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => [
'host' => '/var/run/redis/redis-server.sock',
'port' => 0,
'dbindex' => 0,
'password' => 'secret',
'timeout' => 1.5,
],
Update the redis configuration in /etc/redis/redis.conf accordingly. I.e. uncomment Unix socket options and ensure the “socket” and “port” settings match your Nextcloud configuration.
Be sure to set the right permissions on redis.sock so that your webserver can read and write to it. For this you typically have to add the webserver user to the redis group:
sudo usermod -a -G redis www-data
Finally, restart Redis, NGINX and PHP-FPM:
sudo systemctl restart redis-server nginx php8.0-fpm