Ramblings of an aging IT geek
← Ramblings of an aging IT geek
homelab

nextcloud, the fourth attempt, the one that stuck

After three half-hearted Nextcloud installs that I let rot, the setup that finally earned my trust: proper database, dedicated reverse proxy, real backups and the boring maintenance that keeps it alive.

A server rack hosting a self-managed cloud setup

I have installed Nextcloud at least three times before this and abandoned it every single time. Not because it's bad, but because I always set it up the lazy way, got it just working enough to be impressed for an afternoon, and then watched it slowly fall over until I couldn't be bothered to fix it. The SQLite database that's fine for testing and miserable in real use. The PHP that was never quite tuned. The upgrade I put off until it became frightening. Each time, the install rotted because I'd treated it as a toy.

This time I decided to do it properly, which mostly meant doing the boring things I'd skipped every time before.

a real database

The first and biggest change: a proper database. Nextcloud will happily run on SQLite and it's the path of least resistance, which is exactly why it's a trap. The moment you have real files, real syncing clients, and more than one person, SQLite's single-writer nature turns into file locks and timeouts and "operation in progress" errors that make the whole thing feel broken. I moved to MariaDB in its own container, gave Nextcloud its own database and user, and the performance difference was not subtle.

  db:
    image: mariadb:10.6
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED --log-bin=ROW
    environment:
      MARIADB_DATABASE: nextcloud
      MARIADB_USER: nextcloud
      MARIADB_PASSWORD_FILE: /run/secrets/nc_db_pass
    volumes:
      - ./db:/var/lib/mysql

I also added Redis for file locking and caching, which sounds like over-engineering for a household, and isn't. Transactional file locking on Redis is what stops the sync clients from tripping over each other, and it quietened a whole class of intermittent errors I used to just live with.

the reverse proxy and the trusted domains

Every previous install, I'd fought Nextcloud about what its own URL was. It's very particular. If the trusted_domains and overwrite.cli.url and the proxy headers don't all agree, you get login loops, broken share links, or that special joy where the desktop client connects but the web UI insists you're an imposter.

A self-hosted cloud dashboard showing files and sync status

The fix was to stop hand-waving and write it down explicitly. Traefik terminates TLS and forwards to the container, and the container's config states, plainly, who it thinks it is.

'trusted_domains' => ['cloud.example.lan'],
'overwrite.cli.url' => 'https://cloud.example.lan',
'overwriteprotocol' => 'https',
'trusted_proxies' => ['172.18.0.0/16'],

Once the app and the proxy agreed on reality, every weird redirect bug I'd blamed on Nextcloud over the years quietly disappeared. They were never Nextcloud's fault. They were mine, for not telling it the truth.

backups that I've actually tested

Here's the part that separates "an install" from "infrastructure": I can lose the whole machine and get it back. Nextcloud's state is three things, and you need all three consistent with each other, or your restore is a museum of mismatched bits.

  • The data directory, the actual files, which I back up with a nightly snapshot to off-site storage.
  • The database, dumped with mysqldump while the instance is in maintenance mode so the dump and the files agree.
  • The config, the config.php and the secrets, kept with the rest of the stack's configuration.

The crucial habit is maintenance mode around the database dump. Snapshot the files and dump the database at different moments under load and you get a backup that's subtly inconsistent, the worst kind, the kind that restores cleanly and then behaves strangely. I put the instance into maintenance mode, dump, snapshot, drop out of maintenance mode, all from a small script on a timer. And, the bit everyone skips, I have actually restored it onto a spare machine to prove the backup works. A backup you've never restored is a rumour.

the maintenance I no longer skip

The reason my earlier installs died wasn't a dramatic failure, it was neglect. Nextcloud needs a background cron job to do its housekeeping, and run via the web AJAX trigger it's flaky. I set up a proper cron container that runs cron.php every five minutes, so maintenance happens whether or not anyone's using the web UI.

Upgrades I now do deliberately, one major version at a time, with a backup taken first and the release notes actually read. Nextcloud won't let you skip major versions and punishes you if you try, so I bump on purpose rather than letting the gap grow into something I'm scared of. That fear of the upgrade was what killed every previous install. Take it in small, regular, unscary steps and it never becomes the thing you avoid.

was it worth it

Yes, plainly. The difference between this install and the three corpses before it isn't the software, it's that I treated it like something I depend on instead of something I was trying out. Real database, honest proxy config, tested backups, scheduled maintenance. None of it clever, all of it boring, and the boring is exactly what makes it still be running in a year. My files sync, the calendar works, the share links don't break, and I no longer flinch when an upgrade notification appears. That's the whole win.