Add Unraid flash config to Borg preflight
This commit is contained in:
@@ -11,16 +11,18 @@ Use Borg as the single backup system for:
|
||||
- critical file-backed application data
|
||||
- secrets, keys, and reverse-proxy state
|
||||
- database dumps generated before each Borg backup
|
||||
- Unraid flash configuration artifacts generated before each Borg backup
|
||||
|
||||
Do not back up raw live database storage directories as the primary recovery artifact.
|
||||
|
||||
## Strategy
|
||||
|
||||
1. A pre-backup dump script runs on the host and writes fresh dumps to `/mnt/user/backups/borg/dumps/latest`.
|
||||
1. A pre-backup dump script runs on the host and writes fresh dumps plus `unraid-flash-config.tar.gz` to `/mnt/user/backups/borg/dumps/latest`.
|
||||
2. Borg backs up `/local/borg-dumps` plus the critical mounted paths below.
|
||||
3. Borg retention handles history; the dump directory itself keeps only the latest artifacts.
|
||||
|
||||
The inclusion of `/local/secrets` is intentional: Borg is expected to cover disaster recovery for selected secret material as part of the current homelab restore strategy.
|
||||
The Unraid flash configuration archive is intentional as well and must be treated as secret backup material.
|
||||
|
||||
## Service Inventory
|
||||
|
||||
@@ -41,6 +43,7 @@ The inclusion of `/local/secrets` is intentional: Borg is expected to cover disa
|
||||
| Borg UI | SQLite dump + self-backup | `/local/borg-dumps`, `/local/appdata/borg-ui/data` |
|
||||
| Komodo | config + Mongo dump | `/local/borg-dumps`, `/local/appdata/komodo/periphery`, `/local/appdata/komodo/core` |
|
||||
| GitOps host automation | repo clone + Komodo workspaces + host-check state | `/local/services/homelab-infra`, `/local/services/stacks`, `/local/services/posture-check` |
|
||||
| Unraid OS flash | generated config archive | `/local/borg-dumps/unraid-flash-config.tar.gz` plus checksum and manifest |
|
||||
| Nextcloud | DB dump + file data | `/local/borg-dumps`, `/local/appdata/nextcloud/html`, `/local/nextcloud/data` |
|
||||
| Grafana | SQLite dump + file data | `/local/borg-dumps`, `/local/appdata/grafana` |
|
||||
| Filebrowser | file-backed state dump + file data | `/local/borg-dumps`, `/local/appdata/filebrowser` |
|
||||
@@ -83,6 +86,7 @@ The live Unraid User Scripts execute repo scripts from `/mnt/user/services/homel
|
||||
- Komodo MongoDB
|
||||
- SQLite: `gitea`, `vaultwarden`, `speedtest-tracker`, `borg-ui`, `grafana`
|
||||
- File-backed state: `filebrowser.bolt.dump`
|
||||
- Unraid flash config: `unraid-flash-config.tar.gz` plus `unraid-flash-config.tar.gz.sha256`
|
||||
|
||||
## Explicitly Not Backed Up as Raw Live DB Files
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ Fresh dump artifacts are written to:
|
||||
|
||||
Borg UI should include `/local/borg-dumps` as a backup source.
|
||||
|
||||
The dump set also includes `unraid-flash-config.tar.gz`, a host-generated
|
||||
archive of `/boot/config` plus checksum and manifest. Treat this archive as
|
||||
secret backup material.
|
||||
|
||||
## Notes
|
||||
|
||||
- The script is written for host execution where `docker` is available.
|
||||
|
||||
@@ -17,7 +17,7 @@ It should **not** be implemented as a Borg UI inline hook in the current design.
|
||||
`pre-borg.sh` currently chains the host-side checks:
|
||||
|
||||
- `services/posture-check/posture-check.sh`
|
||||
- `ops/borg-ui/scripts/pre-backup-dumps.sh`
|
||||
- `ops/borg-ui/scripts/pre-backup-dumps.sh` including the Unraid flash config archive
|
||||
- `ops/restore-tests/check-restore-freshness.sh`
|
||||
|
||||
The dump step assumes:
|
||||
@@ -56,9 +56,10 @@ The intended sequence is:
|
||||
|
||||
1. Host wrapper checks posture.
|
||||
2. Host script refreshes `latest` dump artifacts.
|
||||
3. Freshness check verifies expected dumps.
|
||||
4. Borg UI backs up `/local/borg-dumps` together with the rest of `critical_infra`.
|
||||
5. Borg history preserves dump history, so the host only needs to keep the most recent dump set.
|
||||
3. Host script writes `unraid-flash-config.tar.gz` plus checksum and manifest into the same dump set.
|
||||
4. Freshness check verifies expected dumps and the flash config archive.
|
||||
5. Borg UI backs up `/local/borg-dumps` together with the rest of `critical_infra`.
|
||||
6. Borg history preserves dump history, so the host only needs to keep the most recent dump set.
|
||||
|
||||
## Current dump target
|
||||
|
||||
|
||||
@@ -155,6 +155,56 @@ dump_file_copy() {
|
||||
atomic_write "$output" "$tmp"
|
||||
}
|
||||
|
||||
backup_unraid_flash_config() {
|
||||
output="$LATEST_DIR/unraid-flash-config.tar.gz"
|
||||
checksum="$LATEST_DIR/unraid-flash-config.tar.gz.sha256"
|
||||
manifest="$LATEST_DIR/unraid-flash-config.manifest.txt"
|
||||
tmp="$TMP_DIR/unraid-flash-config.tar.gz.tmp"
|
||||
tmp_checksum="$TMP_DIR/unraid-flash-config.tar.gz.sha256.tmp"
|
||||
tmp_manifest="$TMP_DIR/unraid-flash-config.manifest.txt.tmp"
|
||||
|
||||
if [ ! -d /boot/config ]; then
|
||||
warn "Skipping Unraid flash config backup because /boot/config is missing"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Backing up Unraid flash configuration from /boot/config"
|
||||
rm -f "$tmp" "$tmp_checksum" "$tmp_manifest"
|
||||
|
||||
tar -C /boot \
|
||||
--exclude='config/plugins/*/*.txz' \
|
||||
--exclude='config/plugins/*/*.tgz' \
|
||||
--exclude='config/plugins/*/*.tar' \
|
||||
--exclude='config/plugins/*/*.tar.*' \
|
||||
--exclude='config/plugins/*/*.zip' \
|
||||
--exclude='config/plugins/*/*.md5' \
|
||||
-czf "$tmp" config
|
||||
chmod 600 "$tmp"
|
||||
atomic_write "$output" "$tmp"
|
||||
|
||||
(
|
||||
cd "$LATEST_DIR"
|
||||
sha256sum "$(basename "$output")"
|
||||
) > "$tmp_checksum"
|
||||
chmod 600 "$tmp_checksum"
|
||||
atomic_write "$checksum" "$tmp_checksum"
|
||||
|
||||
{
|
||||
printf 'created_utc=%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
printf 'host=%s\n' "$(hostname)"
|
||||
if [ -f /etc/unraid-version ]; then
|
||||
sed 's/^/unraid_/' /etc/unraid-version
|
||||
fi
|
||||
printf 'source=/boot/config\n'
|
||||
printf 'archive=%s\n' "$(basename "$output")"
|
||||
printf 'checksum=%s\n' "$(basename "$checksum")"
|
||||
printf 'note=%s\n' 'Contains Unraid configuration and must be treated as secret backup material.'
|
||||
printf 'excluded=%s\n' 'downloadable plugin package archives under /boot/config/plugins/*/'
|
||||
} > "$tmp_manifest"
|
||||
chmod 600 "$tmp_manifest"
|
||||
atomic_write "$manifest" "$tmp_manifest"
|
||||
}
|
||||
|
||||
dump_optional_pg_db() {
|
||||
container="$1"
|
||||
password="$2"
|
||||
@@ -219,6 +269,8 @@ dump_mongo_container() {
|
||||
main() {
|
||||
need_cmd docker
|
||||
need_cmd sqlite3
|
||||
need_cmd tar
|
||||
need_cmd sha256sum
|
||||
ensure_dirs
|
||||
|
||||
# Shared PostgreSQL 17
|
||||
@@ -272,6 +324,10 @@ main() {
|
||||
# MongoDB
|
||||
dump_mongo_container "komodo-mongo" "$LATEST_DIR/komodo-mongo.archive.gz"
|
||||
|
||||
# Unraid USB flash configuration. This is generated into the existing dump
|
||||
# set so Borg carries it off-site together with the database artifacts.
|
||||
backup_unraid_flash_config
|
||||
|
||||
log "Finished refreshing dump set in $LATEST_DIR"
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ $checks = @(
|
||||
@{ Name = "gitea.sqlite.dump"; Path = Join-Path $DumpRoot "gitea.sqlite.dump" },
|
||||
@{ Name = "vaultwarden.sqlite.dump"; Path = Join-Path $DumpRoot "vaultwarden.sqlite.dump" },
|
||||
@{ Name = "speedtest-tracker.sqlite.dump"; Path = Join-Path $DumpRoot "speedtest-tracker.sqlite.dump" },
|
||||
@{ Name = "filebrowser.bolt.dump"; Path = Join-Path $DumpRoot "filebrowser.bolt.dump" }
|
||||
@{ Name = "filebrowser.bolt.dump"; Path = Join-Path $DumpRoot "filebrowser.bolt.dump" },
|
||||
@{ Name = "unraid-flash-config.tar.gz"; Path = Join-Path $DumpRoot "unraid-flash-config.tar.gz" }
|
||||
)
|
||||
|
||||
$reportChecks = @(
|
||||
|
||||
@@ -34,7 +34,8 @@ for dump in \
|
||||
gitea.sqlite.dump \
|
||||
vaultwarden.sqlite.dump \
|
||||
speedtest-tracker.sqlite.dump \
|
||||
filebrowser.bolt.dump; do
|
||||
filebrowser.bolt.dump \
|
||||
unraid-flash-config.tar.gz; do
|
||||
path="$DUMP_ROOT/$dump"
|
||||
if [ ! -f "$path" ]; then
|
||||
critical+=("DUMP_MISSING $dump")
|
||||
|
||||
Reference in New Issue
Block a user