Skip to content

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.

SourceApp
/volume1/docker-ssd/qbittorrent/appdataqbittorrent
/volume1/docker-ssd/speedtest-tracker/configspeedtest-tracker
/volume1/docker-ssd/upsnap/dataupsnap
/volume1/docker-ssd/qui-bittorrent/quiqui
/volume1/docker-ssd/qbitwebui/dataqbitwebui
/volume1/docker-ssd/anibridge/dataanibridge
volume rustatio_datarustatio
volume arcane-agent-dataarcane-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.

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-nas

The 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.

AppKeyWhat breaks on rotation
speedtest-trackerSPEEDTEST_TRACKER_APP_KEYEncrypted values in the SQLite DB
qbitwebuiQBITWEBUI_ENCRYPTION_KEYStored 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.

Archives are plain gzipped tarballs, prefixed backup/<service>/.

Terminal window
cd /volume1/docker-ssd/infrastructure-toolkit/docker-compose-apps/ugreen-nas
tar tzf /volume2/docker-apps-backup/ugreen-nas-latest.tar.gz | head # inspect first
docker compose stop <service> # never gluetun
mkdir -p /volume2/docker-apps-backup/restore-staging
tar 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 deleted
mv /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.

Terminal window
docker compose logs backup
tar tzf /volume2/docker-apps-backup/ugreen-nas-latest.tar.gz | awk -F/ '{print $2}' | sort -u

The last command lists covered services — all nine should appear. Worth re-running after adding any app to the stack.