This guide walks you step by step until you get Nextcloud published as a service on the Edge that can be accessed on the Internet.
Make sure that all of the mandatory prerequisites have been met before progressing further.
Name | Source | Description |
---|---|---|
<CONTAINER_NAME> | User input | The name of the Linux container where the app will be running. |
<DB_PASSWORD> | User input | The password for logging in with the nextcloud PostgreSQL user. |
<IP_ADDRESS_INET_GW> | Linux VPS Admin Panel | The static and public IPv4 address of the Internet Gateway. |
<IP_ADDRESS_SERVER__VPN> | User input | The IPv4 address of the server on the Edge in the VPN, e.g. 10.5.5.2 . |
<NEXTCLOUD_WEBSITE_DOMAIN> | User input | The domain name for the A record created with your domain registrar, e.g. mynextcloud42.net . |
Going through all the steps in this guide shall take around 90 min. with decent Internet connection.
Run the following commands on your server on the Edge.
incus launch images:debian/bullseye/cloud <CONTAINER_NAME>
incus exec <CONTAINER_NAME> bash
ip link set dev eth0 mtu 1000
Changing the MTU for the network interface is not persisted. After rebooting the container the old value will be used.
apt update && apt upgrade
apt install nginx postgresql postgresql-contrib \
apt-transport-https lsb-release ca-certificates curl \
fish unzip wget cron ffmpeg
Make sure that following settings will be reflected in /etc/postgresql/13/main/postgresql.conf
.
max_connections = 100
shared_buffers = 2GB
effective_cache_size = 6GB
maintenance_work_mem = 512MB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 5242kB
min_wal_size = 1GB
max_wal_size = 4GB
The configuration is generated by a tool. It uses an assumption for the "machine" hosting the database. This server machine is supposed to have 2 CPU cores, RAM 8Gb and SSD storage. You can generate more personalised configurations.
systemctl restart postgresql
systemctl status postgresql
You should see that the database systemd service is successfully started and running.
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
apt update && apt install php8.1 php8.1-common php8.1-fpm \
php8.1-pgsql php8.1-curl php8.1-dom php8.1-gd \
php8.1-mbstring php8.1-simplexml php8.1-xmlreader \
php8.1-xmlwriter php8.1-zip php8.1-bz2 php8.1-intl \
php8.1-ldap php8.1-smbclient php8.1-imap php8.1-bcmath \
php8.1-gmp php8.1-imagick libmagickcore-6.q16-6-extra
cd /tmp
wget https://download.nextcloud.com/server/releases/nextcloud-29.0.2.zip
unzip nextcloud-29.0.2.zip -d /var/www
chown -R www-data:www-data /var/www/nextcloud
Search for each of the settings below in /etc/php/8.1/fpm/php.ini
and re-define their values accordingly.
memory_limit=512M
upload_max_filesize=4092M
post_max_size=25M
Uncomment the following lines in /etc/php/8.1/fpm/pool.d/www.conf
.
...
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
...
Enable video preview through ffmpeg
in /var/www/nextcloud/config/config.php
by adding the following configuration values as elements to the $CONFIG
array.
...
'enable_previews' => true,
'enabledPreviewProviders' =>
array (
0 => 'OC\\Preview\\Movie',
1 => 'OC\\Preview\\PNG',
2 => 'OC\\Preview\\JPEG',
3 => 'OC\\Preview\\GIF',
4 => 'OC\\Preview\\BMP',
5 => 'OC\\Preview\\XBitmap',
6 => 'OC\\Preview\\MP3',
7 => 'OC\\Preview\\MP4',
8 => 'OC\\Preview\\TXT',
9 => 'OC\\Preview\\MarkDown',
10 => 'OC\\Preview\\PDF',
),
...
systemctl restart php8.1-fpm
systemctl status php8.1-fpm
The systemd service should be up and running.
sudo -i -u postgres psql
CREATE USER nextcloud WITH PASSWORD '<DB_PASSWORD>';
CREATE DATABASE nextcloud TEMPLATE template0 ENCODING 'UNICODE';
ALTER DATABASE nextcloud OWNER TO nextcloud;
GRANT ALL PRIVILEGES ON DATABASE nextcloud TO nextcloud;
\q
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/nextcloud.key \
-out /etc/ssl/certs/nextcloud.crt
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
Add a new nginx website.
vi /etc/nginx/sites-available/nextcloud
Populate it with the configuration below.
Don't forget to substitute all occurrences of
<NEXTCLOUD_WEBSITE_DOMAIN>
with the value matching your setup.
upstream php-handler {
server unix:/var/run/php/php8.1-fpm.sock;
}
# Set the `immutable` cache control options only for assets with a cache busting `v` argument
map $arg_v $asset_immutable {
"" "";
default ", immutable";
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name drive.<NEXTCLOUD_WEBSITE_DOMAIN>;
# Path to the root of your installation
root /var/www/nextcloud;
# Use Mozilla's guidelines for SSL/TLS settings
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
ssl_certificate /etc/ssl/certs/nextcloud.crt;
ssl_certificate_key /etc/ssl/private/nextcloud.key;
# Prevent nginx HTTP Server Detection
server_tokens off;
# 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 and increase upload timeout:
client_max_body_size 512M;
client_body_timeout 300s;
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 text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm 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;
# The settings allows you to optimize the HTTP2 bandwidth.
# See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/
# for tuning hints
client_body_buffer_size 512k;
# 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-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "noindex, nofollow" 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;
# Set .mjs and .wasm MIME types
# Either include it in the default mime.types list
# and include that list explicitly or add the file extension
# only for Nextcloud like below:
include mime.types;
types {
text/javascript js mjs;
application/wasm wasm;
}
# 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 asets,
# `/updater`, `/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(?:$|/) {
# Required for legacy support
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri;
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;
fastcgi_max_temp_file_size 0;
}
# Serve static files
location ~ \.(?:css|js|mjs|svg|gif|png|jpg|ico|wasm|tflite|map|ogg|flac)$ {
try_files $uri /index.php$request_uri;
# HTTP response headers borrowed from Nextcloud `.htaccess`
add_header Cache-Control "public, max-age=15778463$asset_immutable";
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "noindex, nofollow" always;
add_header X-XSS-Protection "1; mode=block" always;
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;
}
}
ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/
systemctl restart nginx
systemctl status nginx
The systemd service should be up & running.
On the server on Edge run the following command.
incus config device add <CONTAINER-NAME> port8080_443 \
proxy listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:443
ssh mycelium@<IP_ADDRESS__INET_GW>
sudo su -
Append the following reverse proxy configuration to /etc/caddy/Caddyfile
.
drive.<NEXTCLOUD_WEBSITE_DOMAIN> {
tls tls@<NEXTCLOUD_WEBSITE_DOMAIN>
# Use the IP address of the host within the Wireguard VPN.
reverse_proxy <IP_ADDRESS_SERVER__VPN>:8080 {
# Use https with a self signed cert between Caddy and Nextcloud
transport http {
tls
tls_insecure_skip_verify
}
}
}
systemctl restart caddy # "reload" will work as well
systemctl status caddy
The systemd service should be up & running.
Make sure that you have created an "A" domain record for drive.<NEXTCLOUD_WEBSITE_DOMAIN> with your domain registrar and you have waited for this record to become active.
If the "A" record is not updated yet, Caddy will not be able to serve HTTPS requests because of Let's Encrypt (An SSL certificate authority) failing to verify the validity (ownership over) of the domain.
You should be able to reach the Nextcloud web installer by using
https://drive.<NEXTCLOUD_WEBSITE_DOMAIN>
in the address bar of a web browser.
In a web browser navigate to https://drive.<NEXTCLOUD_WEBSITE_DOMAIN>
. Follow the guidance and provide the requested input.
Guidance
nextcloud
.localhost
./var/www/nextcloud/data
You can safely skip installing the Recommended Apps proposed by the web installer. You can do that later.
After the web installer completes, you should be able to log in with your Nextcloud instance as an administrative user and Nextcloud will be fully functional.
In your Linux container edit the crontab of the default Web server user (www-data
):
sudo crontab -u www-data -e
Add the following line:
*/5 * * * * php -f /var/www/nextcloud/cron.php
We can verify if the cron job has been added and scheduled running
sudo crontab -u www-data -l
Log in as an Administrator with Nextcloud. Choose Cron in the Background jobs section of the Admin settings page at Setting > Administration > Basic settings > Background jobs
.
At this point many of us find Nextcloud so slow that it is unusable. Further performance improvements need to be done. Follow the guidance below to make Nextcloud a useful tool.
From the server on the Edge log in with the root
user of the Linux container.
incus exec <CONTAINER_NAME> bash
apt update && apt install php8.1-apcu
Add the following configuration to /etc/php/8.1/cli/php.ini
. Note that there may be an existing [opcache]
section but all configuration values may be commented out. We would recommend reusing the existing section.
apc.enable_cli=1
; source ArchWiki
[opcache]
opcache.enable = 1
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 10000
opcache.memory_consumption = 128
opcache.save_comments = 1
opcache.revalidate_freq = 1
apt update && apt install redis-server php8.1-redis
Add the following elements to the $CONFIG
array declared in /var/www/nextcloud/config/config.php
in order to start using redis
and php-apcu
.
'memcache.local' => '\OC\Memcache\APCu',
'memcache.locking' => '\OC\Memcache\Redis',
'memcache.distributed' => '\OC\Memcache\Redis',
'redis' => [
'host' => 'localhost',
'port' => 6379,
],
In /etc/php/8.1/fpm/pool.d/www.conf
append the following configuration.
pm.max_children = 16
pm.start_servers = 3
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500
systemctl restart php8.1-fpm
systemctl restart nginx
You can also disable and remove some of the applications that you don't use. This will also contribute towards owning a more performant Nextcloud instance.
For example, we usually disable the following applications.
Then these applications get removed as well.
Nextloud should feel snappier and more useful now.