Open Source

Self-host Nextcloud, Properly

·2 min read

Nextcloud is the open-source alternative to Dropbox + Google Docs + Google Calendar. Install instructions on the official site will get you to "it loads." This is the additional config that gets you to "production-ready" — fast, supervised, observable, and with backups that work.

The stack

nginx (web), PHP-FPM 8.3 (application), PostgreSQL 16 (database), Redis (file locking + sessions), APCu (local cache), Certbot (TLS). All Debian-packaged.

apt install -y nginx postgresql redis-server \
    php8.3-fpm php8.3-pgsql php8.3-redis php8.3-apcu \
    php8.3-gd php8.3-curl php8.3-zip php8.3-xml php8.3-mbstring \
    php8.3-bcmath php8.3-gmp php8.3-imagick certbot python3-certbot-nginx

Database

sudo -u postgres createuser nextcloud --pwprompt
sudo -u postgres createdb -O nextcloud nextcloud

Filesystem layout

mkdir -p /var/www/nextcloud /srv/nextcloud-data
chown -R www-data:www-data /var/www/nextcloud /srv/nextcloud-data
chmod 0750 /srv/nextcloud-data

Keep the data directory outside the web root. nginx must never be able to serve user files directly.

nginx config snippet

upstream php-handler { server unix:/run/php/php8.3-fpm.sock; }

server {
    listen 443 ssl http2;
    server_name cloud.example.com;

    root /var/www/nextcloud;
    client_max_body_size 10G;

    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Content-Type-Options nosniff always;
    add_header Referrer-Policy no-referrer always;

    location = /robots.txt { allow all; log_not_found off; access_log off; }

    location / {
        rewrite ^ /index.php;
    }

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

    location ~ \.php(?:$|/) {
        include fastcgi_params;
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTPS on;
        fastcgi_pass php-handler;
        fastcgi_read_timeout 600s;
    }
}

config.php tuning

'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,
],
'default_phone_region' => 'US',
'maintenance_window_start' => 1,    // run heavy jobs at 1 AM
'log_type' => 'file',
'logfile' => '/var/log/nextcloud/nextcloud.log',

Background jobs

Nextcloud has a cron job runner. The web-based AJAX runner is fine for tiny installs; for anything real, use system cron:

(crontab -u www-data -l 2>/dev/null; \
 echo "*/5 * * * * php -f /var/www/nextcloud/cron.php") | crontab -u www-data -

PHP-FPM pool sizing

; /etc/php/8.3/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 30      ; total ≈ available_RAM / 80MB
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 500

Backups

Three things to back up: the database, the data directory, and config/config.php. Put the install into maintenance mode first to ensure a consistent snapshot:

occ() { sudo -u www-data php /var/www/nextcloud/occ "$@"; }

occ maintenance:mode --on
pg_dump -U nextcloud nextcloud | gzip > /backups/nc-$(date +%F).sql.gz
rsync -a /srv/nextcloud-data/ /backups/nextcloud-data/
occ maintenance:mode --off

Related