I have installed Nextcloud three times now. The first time it was the snap, which worked until it didn't and then fought me about where its data lived. The second time it was the all-in-one Docker image, which is genuinely fine until you want to deviate from its assumptions by even a degree. This is the third time, with each piece run as a container I actually understand, and it's the first version I trust enough to put my photos and my Obsidian vault behind.
The reason the earlier attempts soured wasn't Nextcloud being bad. It was me treating it as a single black box and then being surprised when the black box had opinions. Once I broke it into its actual parts, database, cache, app, web server, reverse proxy, every problem became a normal, debuggable problem rather than a mysterious one.
the database is not optional
The single biggest performance change was moving off SQLite, which the easy installers quietly give you, and onto Postgres. SQLite is fine for a single user kicking the tyres. The moment you have the desktop client, the mobile client, and a couple of background jobs all touching the database at once, it locks and everything feels like wading through treacle.
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD_FILE: /run/secrets/nc_db_pass
volumes:
- ./db:/var/lib/postgresql/data
secrets:
- nc_db_pass
redis:
image: redis:7-alpine
command: redis-server --save "" --appendonly no
Redis is the other piece nobody mentions until things are slow. Nextcloud uses it for file locking and for caching, and without it you get spurious "another process is editing this file" errors and a lot of unnecessary filesystem churn. The Redis config above deliberately turns off persistence: it's a cache, I don't care if it loses its contents on restart, and not writing to disk keeps it quick.
the config.php changes that actually matter
After the app is up, there's a short list of settings that move it from "works" to "feels good". These go in config/config.php:
'memcache.local' => '\OC\Memcache\APCu',
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => [
'host' => 'redis',
'port' => 6379,
],
'default_phone_region' => 'GB',
'maintenance_window_start' => 1,
APCu for the local cache, Redis for distributed and locking. The default_phone_region one looks trivial but it silences a permanent warning in the admin panel and stops the contacts app from sulking about phone numbers. The maintenance_window_start tells Nextcloud when it's allowed to run its heavy background jobs, in my case 1am UTC, so it isn't trying to rebuild previews while I'm uploading from my phone.
cron, not ajax
The default background-job mode runs tasks when someone happens to load a page. That means on a personal instance, where days pass between visits, the jobs simply don't run, and then everything you assumed was happening (file scanning, notifications, cleanup) silently isn't. Switch it to real cron:
*/5 * * * * docker exec -u www-data nextcloud-app php /var/www/html/cron.php
Set the mode to "Cron" in the admin settings to match, and the little jobs that keep the instance tidy actually run on schedule.
the reverse proxy and the trusted bits
I put Caddy in front, mostly because its config is short enough to read in one sitting and it handles certificates without me thinking about it. The only Nextcloud-side gotcha is telling it that it's behind a proxy, otherwise it generates http links and the mobile app refuses to connect:
'overwriteprotocol' => 'https',
'overwrite.cli.url' => 'https://cloud.i0.pm',
'trusted_proxies' => ['172.18.0.0/16'],
Get the trusted_proxies range wrong and you'll spend an evening wondering why rate limiting thinks every request comes from the same address. It does, because without that line every request appears to come from the proxy.
was it worth it
Honestly, yes, but with an asterisk. Nextcloud is not lightweight and it never will be. It's a PHP application doing a lot of things, and if you want something that just syncs files you'd be happier with Syncthing and an afternoon off. What Nextcloud gives me that Syncthing doesn't is the photo timeline on my phone, calendar and contacts I own, and a web view I can hand to family members who will never touch a terminal.
The lesson from three attempts is dull and true: the thing isn't fragile, my mental model was. Run it as its real components, give it a proper database and a cache, let cron do its job, and it sits there quietly doing exactly what I wanted the first two times. I'll check back in six months and see whether I still believe that. Given the track record, set a reminder.