On this page
Your NAS backs up everything except the things running on it. The photos in Immich, the configuration in Home Assistant, the scanned documents in Paperless-ngx all live in Docker volumes on the NAS, and a backup that lives on the same box is not a backup. This guide pushes that app data off the NAS to an append-only restic server on a cluster node, so the NAS can add snapshots but never delete them, and it covers the one thing the laptop version did not have to worry about: a live database.
I found this gap the same embarrassing way as the last one. I had built a tidy backup that protected my laptop and my PCs, and then I looked at the NAS itself and realized the services I would actually miss, years of photos and every scanned document, had nothing protecting them but the disks they sat on.
The backup that has no backup
It is easy to assume the NAS is the safe place, because it is where the backups go. But the applications hosted on the NAS are just more data, and that data is inside the failure domain of the one machine. If a disk stack dies or ransomware reaches the NAS, the photos and the documents go with it, and so does any copy sitting on the same box. The apps that feel the most permanent are often the least protected.
The rule is the same one behind every good backup: it has to survive the thing it is protecting. That means the copy belongs on a different machine, and ideally one the NAS cannot reach in to delete.
Same pattern, pointed the other way
If you have already set up the append-only laptop backup, you have done ninety percent of this. It is the identical restic rest-server setup, with the roles swapped: this time the NAS is the client and a cluster node hosts the append-only server. The NAS runs a nightly restic job that pushes the app volumes to rest:http://10.0.0.60:8501/nas, and because the server runs with --append-only, the NAS can write new snapshots but cannot delete or rewrite old ones.
The nas-restic-rest-server playbook is the whole setup. To apply it here, change the server’s host from the NAS to a cluster node, and change what the client backs up from $HOME to the app data paths below. The append-only flag, the --private-repos repo-equals-username rule, and the RESTIC_REST_USERNAME/RESTIC_REST_PASSWORD credentials all work exactly the same.
Replace 10.0.0.60 with your cluster node’s IP, 8501 with the port it publishes, the repo user nas with a name for this machine, and the volume paths with wherever your apps actually store data. If a value looks specific to one machine, it’s a placeholder to change, not a literal to copy.
The catch a laptop never had: live databases
A laptop backup is mostly files sitting still. App data is not. Two of these three services keep a running database, and that changes the rules. A database being written to is not crash-consistent on disk, so if restic copies the database directory mid-write, you can capture a half-finished state that restores into corruption. The backup will look perfectly healthy right up until the day you need it.
Each app has a correct way to handle this:
- Immich keeps photo metadata and file paths in PostgreSQL and never rescans the library folder, so the database is not optional. Immich’s own documentation is blunt: “database backups are essential.” Back up the
library,upload, andprofilefolders and apg_dumpallof the database, and take the database dump first (or stop the server) so the two agree with each other. - Paperless-ngx stores documents as files but keeps its index in a database too. Its maintainers recommend the built-in
document_exporter, which writes documents, metadata, and database contents into one folder that restic can then back up consistently. - Home Assistant is the easy one: its configuration is plain YAML and files, so a file-level backup of the config directory is genuinely fine.
When I first built this, the nightly job backed up the Immich photo tree but not a dump of its Postgres database, because adding the dump step needed a container exec I had not wired up yet. The photos were covered; the database was not. I am calling that out because it is exactly the trap: a backup that captures the files and quietly skips the database is the one that surprises you at restore time. Add the dump step, or your “Immich backup” is a folder of orphaned files.
Practically, the nightly script runs each app’s dump command first, then hands restic the dump files plus the file trees in one snapshot.
# 1. dump the databases to a staging dir (runs inside/against each app)
pg_dumpall ... > /volume1/backups/staging/immich.sql
# 2. one restic snapshot: the dumps + the file trees
source /volume1/Docker/restic-rest/env
restic backup \
/volume1/backups/staging \
/volume1/Docker/immich/library \
/volume1/Docker/homeassistant/config \
/volume1/Docker/paperless/media
restic check
curl -fsS "$HEARTBEAT_URL" >/dev/null
Prune on the server, never the client
Append-only has one consequence people trip over: the client is not allowed to delete, which means it cannot prune its own old snapshots either. That is the point, not a bug. Retention runs on the server instead, on its own schedule, with a credential the NAS never holds. A weekly job on the cluster node runs restic forget --prune with a policy like keep 14 daily, 8 weekly, 6 monthly. The NAS keeps adding; the server alone decides what ages out.
Make a dead backup loud
A backup job that silently stops is worse than one that was never set up, because you think you are covered. The same heartbeat pattern from the laptop guide applies: the script pings a monitor only after the backup and the restic check both succeed, and the monitor alerts if the ping does not arrive. Wire it into whatever you already run, whether that is Prometheus with a staleness alert or an Uptime Kuma push monitor, so a NAS that stops backing itself up tells you within a day.
Prove it comes back
You do not have a backup until you have restored one. Pull a document and a photo out to a scratch directory and checksum them against the originals, and if a database is involved, restore the dump into a throwaway instance and confirm the app starts and can see its data.
source /volume1/Docker/restic-rest/env
restic restore latest --target /tmp/nas-restore --include /volume1/Docker/paperless/media
sha256sum <original-file> /tmp/nas-restore/<original-file>
On the real build this covered the Immich photo tree, the Home Assistant config, and Paperless, and the restore drill matched byte for byte. The database dump is the piece to add before you call it done, and the way you confirm it is done is the same as everything else here: restore it and check.
Related posts:
- Append-only laptop backups with restic — the base pattern this reuses, explained end to end with the paired playbook.
- Immich on Proxmox: private photo backup — the photo service whose database this post insists you dump.
- Paperless-ngx: a searchable archive for every document — the document store on the other end of this backup.
- Home Assistant in a container on your NAS — the config that is the easy one to protect.
- Measure how fast your backups actually restore — the restore-drill discipline, applied to the whole cluster.
- What is Prometheus? — where the “this backup went quiet” alert can live.
Comments
Comments are powered by GitHub Discussions — sign in with a GitHub account to join the conversation.