Backup
Nightly config/state backup for the whole ugreen-nas stack, via
offen/docker-volume-backup.
Archives land in /volume2/docker-apps-backup/ as ugreen-nas-<timestamp>.tar.gz, with
ugreen-nas-latest.tar.gz symlinked to the newest. Runs at 04:00, prunes after 30 days;
all three are overridable in ../../.env.example.
This NAS is not a Proxmox guest, so it sits outside the PBS coverage protecting the rest of the fleet. This job is its only backup.
Coverage
Section titled “Coverage”| Source | App |
|---|---|
/volume1/docker-ssd/qbittorrent/appdata | qbittorrent |
/volume1/docker-ssd/speedtest-tracker/config | speedtest-tracker |
/volume1/docker-ssd/upsnap/data | upsnap |
/volume1/docker-ssd/qui-bittorrent/qui | qui |
/volume1/docker-ssd/qbitwebui/data | qbitwebui |
/volume1/docker-ssd/anibridge/data | anibridge |
volume rustatio_data | rustatio |
volume arcane-agent-data | arcane-agent |
Sources are all on /volume1 and the archive lands on /volume2, so one pool failure
cannot take both. Deliberately excluded: the media under /volume2/plex and
/volume2/metube (re-acquirable, replication territory), and
/home/unknown224/Apps/gluetun (a cache that regenerates).
An app added to the stack needs a matching line in backup.yaml — nothing detects an
uncovered volume for you.
Two lists are easy to conflate: what gets archived is the /backup/* mounts in
backup.yaml, and what gets paused is the containers carrying the
stop-during-backup label. qbittorrent is on both, but only its appdata directory is
archived. The archive destination mounts at /archive, outside /backup, so backups
never nest inside the next one.
That mount list is the only thing keeping media out. Bind mounts are recursive, so a share
mounted inside one of those directories would be pulled in silently —
BACKUP_EXCLUDE_REGEXP is present in backup.yaml for that case, commented out. Until
then, archive size is the signal: a jump into GB territory means something got mounted
that should not have been.
Consistency
Section titled “Consistency”upsnap (PocketBase), speedtest-tracker and qui keep SQLite databases, and
qbittorrent only flushes qBittorrent.conf and BT_backup/ on a clean shutdown —
copying any of them mid-write can produce a file that will not open. Each carries:
labels: - docker-volume-backup.stop-during-backup=ugreen-nasThe scoped value keeps the job away from UGOS’s own containers under
/volume2/@appstore, which share the same Docker socket.
gluetun is never stopped — that orphans the network namespaces of qbittorrent and
rustatio, the failure deunhealth exists to recover from. Stopping those two dependants
is safe; they re-attach on start. Do not add the label to gluetun.
The encryption-key constraint
Section titled “The encryption-key constraint”| App | Key | What breaks on rotation |
|---|---|---|
speedtest-tracker | SPEEDTEST_TRACKER_APP_KEY | Encrypted values in the SQLite DB |
qbitwebui | QBITWEBUI_ENCRYPTION_KEY | Stored qBittorrent credentials |
Rotating either key invalidates every archive taken before the rotation, and the restore will appear to succeed — files land, containers start, decrypted values are garbage. BWS holds only the current value, so the old one is unrecoverable. After rotating, treat that app’s backup history as gone and take a fresh backup immediately.
Restoring
Section titled “Restoring”Archives are plain gzipped tarballs, prefixed backup/<service>/.
cd /volume1/docker-ssd/infrastructure-toolkit/docker-compose-apps/ugreen-nas
tar tzf /volume2/docker-apps-backup/ugreen-nas-latest.tar.gz | head # inspect firstdocker compose stop <service> # never gluetun
mkdir -p /volume2/docker-apps-backup/restore-stagingtar xzf /volume2/docker-apps-backup/<archive>.tar.gz \ -C /volume2/docker-apps-backup/restore-staging backup/<service>
mv <live-path> <live-path>.pre-restore # aside, not deletedmv /volume2/docker-apps-backup/restore-staging/backup/<service> <live-path>
bws run --access-token "$BWS_ACCESS_TOKEN" -- docker compose up -d <service>bws run is required — a bare up -d starts services with empty credentials. Most of
these apps run as PUID=1000 with PGID=10 or 1000, so chown -R after extracting if
the app will not start.
A full-host rebuild needs three things, and only one of them is in the archive:
BWS_ACCESS_TOKEN (stored outside this NAS and outside BWS — the one credential this
job cannot back up), the repo, and the extracted data.
Verify
Section titled “Verify”docker compose logs backuptar tzf /volume2/docker-apps-backup/ugreen-nas-latest.tar.gz | awk -F/ '{print $2}' | sort -uThe last command lists covered services — all nine should appear. Worth re-running after adding any app to the stack.