Merge pull request #64 from naturallaw777/copilot/make-backup-script-role-aware
[WIP] Update backup script to be role-aware
This commit is contained in:
@@ -18,6 +18,8 @@ BACKUP_LOG="/var/log/sovran-hub-backup.log"
|
|||||||
BACKUP_STATUS="/var/log/sovran-hub-backup.status"
|
BACKUP_STATUS="/var/log/sovran-hub-backup.status"
|
||||||
MEDIA_ROOT="/run/media"
|
MEDIA_ROOT="/run/media"
|
||||||
MIN_FREE_GB=10
|
MIN_FREE_GB=10
|
||||||
|
HUB_CONFIG_JSON="/var/lib/sovran-hub/config.json"
|
||||||
|
ROLE_STATE_NIX="/etc/nixos/role-state.nix"
|
||||||
|
|
||||||
# ── Internal drive labels/paths to NEVER use as backup targets ───
|
# ── Internal drive labels/paths to NEVER use as backup targets ───
|
||||||
INTERNAL_LABELS=("BTCEcoandBackup" "sovran_systemsos")
|
INTERNAL_LABELS=("BTCEcoandBackup" "sovran_systemsos")
|
||||||
@@ -125,6 +127,40 @@ for d in flatten(data.get('blockdevices', [])):
|
|||||||
echo "$target"
|
echo "$target"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Detect the configured system role ───────────────────────────
|
||||||
|
#
|
||||||
|
# Priority:
|
||||||
|
# 1. Hub config JSON (/var/lib/sovran-hub/config.json) — "role" key
|
||||||
|
# 2. role-state.nix (/etc/nixos/role-state.nix) — grep for true flag
|
||||||
|
# 3. Default: server_plus_desktop
|
||||||
|
|
||||||
|
detect_role() {
|
||||||
|
local role="server_plus_desktop"
|
||||||
|
|
||||||
|
# 1. Try the Hub config JSON
|
||||||
|
if [[ -f "$HUB_CONFIG_JSON" ]] && command -v python3 &>/dev/null; then
|
||||||
|
local r
|
||||||
|
r=$(python3 -c \
|
||||||
|
"import json,sys; d=json.load(open(sys.argv[1])); print(d.get('role',''))" \
|
||||||
|
"$HUB_CONFIG_JSON" 2>/dev/null || true)
|
||||||
|
if [[ -n "$r" ]]; then
|
||||||
|
echo "$r"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Fall back to parsing role-state.nix
|
||||||
|
if [[ -f "$ROLE_STATE_NIX" ]]; then
|
||||||
|
if grep -q 'roles\.desktop = lib\.mkDefault true' "$ROLE_STATE_NIX" 2>/dev/null; then
|
||||||
|
role="desktop"
|
||||||
|
elif grep -q 'roles\.node = lib\.mkDefault true' "$ROLE_STATE_NIX" 2>/dev/null; then
|
||||||
|
role="node"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$role"
|
||||||
|
}
|
||||||
|
|
||||||
# ── Initialise log file ──────────────────────────────────────────
|
# ── Initialise log file ──────────────────────────────────────────
|
||||||
|
|
||||||
: > "$BACKUP_LOG"
|
: > "$BACKUP_LOG"
|
||||||
@@ -133,6 +169,17 @@ set_status "RUNNING"
|
|||||||
log "=== Sovran_SystemsOS External Hub Backup ==="
|
log "=== Sovran_SystemsOS External Hub Backup ==="
|
||||||
log "Starting backup process…"
|
log "Starting backup process…"
|
||||||
|
|
||||||
|
# ── Detect system role ───────────────────────────────────────────
|
||||||
|
|
||||||
|
ROLE="$(detect_role)"
|
||||||
|
case "$ROLE" in
|
||||||
|
desktop) ROLE_LABEL="Desktop Only" ;;
|
||||||
|
node) ROLE_LABEL="Node (Bitcoin-only)" ;;
|
||||||
|
server_plus_desktop) ROLE_LABEL="Server + Desktop" ;;
|
||||||
|
*) ROLE_LABEL="$ROLE" ;;
|
||||||
|
esac
|
||||||
|
log "Detected role: $ROLE_LABEL"
|
||||||
|
|
||||||
# ── Detect target drive ──────────────────────────────────────────
|
# ── Detect target drive ──────────────────────────────────────────
|
||||||
|
|
||||||
if [[ -n "${BACKUP_TARGET:-}" ]]; then
|
if [[ -n "${BACKUP_TARGET:-}" ]]; then
|
||||||
@@ -190,16 +237,29 @@ log ""
|
|||||||
log "── Stage 2/4: Secrets ───────────────────────────────────────"
|
log "── Stage 2/4: Secrets ───────────────────────────────────────"
|
||||||
mkdir -p "$BACKUP_DIR/secrets"
|
mkdir -p "$BACKUP_DIR/secrets"
|
||||||
|
|
||||||
for SRC in /etc/nix-bitcoin-secrets /var/lib/domains; do
|
if [[ "$ROLE" == "desktop" ]]; then
|
||||||
|
log "Skipping /etc/nix-bitcoin-secrets — not applicable for Desktop Only role."
|
||||||
|
# /var/lib/domains is still backed up if present (hub state)
|
||||||
|
for SRC in /var/lib/domains; do
|
||||||
if [[ -e "$SRC" ]]; then
|
if [[ -e "$SRC" ]]; then
|
||||||
rsync -a --info=progress2 "$SRC" "$BACKUP_DIR/secrets/" 2>&1 | tee -a "$BACKUP_LOG" || \
|
rsync -a --info=progress2 "$SRC" "$BACKUP_DIR/secrets/" 2>&1 | tee -a "$BACKUP_LOG" || \
|
||||||
log "WARNING: Could not copy $SRC — continuing."
|
log "WARNING: Could not copy $SRC — continuing."
|
||||||
else
|
else
|
||||||
log " (not found: $SRC — skipping)"
|
log " (not found: $SRC — skipping)"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
else
|
||||||
|
for SRC in /etc/nix-bitcoin-secrets /var/lib/domains; do
|
||||||
|
if [[ -e "$SRC" ]]; then
|
||||||
|
rsync -a --info=progress2 "$SRC" "$BACKUP_DIR/secrets/" 2>&1 | tee -a "$BACKUP_LOG" || \
|
||||||
|
log "WARNING: Could not copy $SRC — continuing."
|
||||||
|
else
|
||||||
|
log " (not found: $SRC — skipping)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# Hub state files from /var/lib/secrets/
|
# Hub state files from /var/lib/secrets/ (backed up for all roles)
|
||||||
if [[ -d /var/lib/secrets ]]; then
|
if [[ -d /var/lib/secrets ]]; then
|
||||||
mkdir -p "$BACKUP_DIR/secrets/hub-state"
|
mkdir -p "$BACKUP_DIR/secrets/hub-state"
|
||||||
rsync -a --info=progress2 /var/lib/secrets/ "$BACKUP_DIR/secrets/hub-state/" 2>&1 | tee -a "$BACKUP_LOG" || \
|
rsync -a --info=progress2 /var/lib/secrets/ "$BACKUP_DIR/secrets/hub-state/" 2>&1 | tee -a "$BACKUP_LOG" || \
|
||||||
@@ -230,7 +290,9 @@ fi
|
|||||||
|
|
||||||
log ""
|
log ""
|
||||||
log "── Stage 4/4: Wallet and node data (/var/lib/lnd) ──────────"
|
log "── Stage 4/4: Wallet and node data (/var/lib/lnd) ──────────"
|
||||||
if [[ -d /var/lib/lnd ]]; then
|
if [[ "$ROLE" == "desktop" ]]; then
|
||||||
|
log "Skipping Stage 4 (LND wallet data) — not applicable for Desktop Only role."
|
||||||
|
elif [[ -d /var/lib/lnd ]]; then
|
||||||
rsync -a --info=progress2 \
|
rsync -a --info=progress2 \
|
||||||
--exclude='logs/' \
|
--exclude='logs/' \
|
||||||
/var/lib/lnd/ "$BACKUP_DIR/lnd/" 2>&1 | tee -a "$BACKUP_LOG" || \
|
/var/lib/lnd/ "$BACKUP_DIR/lnd/" 2>&1 | tee -a "$BACKUP_LOG" || \
|
||||||
@@ -248,6 +310,7 @@ log "Generating BACKUP_MANIFEST.txt …"
|
|||||||
echo "Sovran_SystemsOS Backup Manifest"
|
echo "Sovran_SystemsOS Backup Manifest"
|
||||||
echo "Generated: $(date)"
|
echo "Generated: $(date)"
|
||||||
echo "Hostname: $(hostname)"
|
echo "Hostname: $(hostname)"
|
||||||
|
echo "Role: $ROLE_LABEL"
|
||||||
echo "Target: $TARGET"
|
echo "Target: $TARGET"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Contents:"
|
echo "Contents:"
|
||||||
|
|||||||
93
docs/manual-backup.md
Normal file
93
docs/manual-backup.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# Sovran Hub — Manual Backup
|
||||||
|
|
||||||
|
The manual backup service copies critical system data from your Sovran Pro to an external USB drive, providing a third copy of your data (your Sovran Pro already maintains an automatic internal backup on its second drive).
|
||||||
|
|
||||||
|
Backups are written to:
|
||||||
|
|
||||||
|
```
|
||||||
|
<USB drive>/Sovran_SystemsOS_Backup/<timestamp>/
|
||||||
|
```
|
||||||
|
|
||||||
|
where `<timestamp>` is formatted as `YYYYMMDD_HHMMSS`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backup Stages
|
||||||
|
|
||||||
|
The script always attempts all four stages, but skips stages that are irrelevant to the system's configured role (see [Per-Role Breakdown](#per-role-breakdown) below).
|
||||||
|
|
||||||
|
| Stage | Directory | Contents |
|
||||||
|
|-------|-----------|----------|
|
||||||
|
| **1/4 — NixOS config** | `/etc/nixos/` | Full NixOS system configuration: `role-state.nix`, `custom.nix`, flake files, and any other config managed by the Hub |
|
||||||
|
| **2/4 — Secrets** | `/etc/nix-bitcoin-secrets`, `/var/lib/domains`, `/var/lib/secrets` | Bitcoin/LND secrets, domain configurations for all web services, and Hub state files |
|
||||||
|
| **3/4 — Home directory** | `/home/` | All user home directories (`.cache/` and Trash are excluded) |
|
||||||
|
| **4/4 — LND wallet data** | `/var/lib/lnd/` | Lightning Network node wallet and channel data (log files excluded) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Per-Role Breakdown
|
||||||
|
|
||||||
|
The script detects the system role at runtime by reading `/var/lib/sovran-hub/config.json` (falling back to `/etc/nixos/role-state.nix`) and adjusts its behaviour accordingly.
|
||||||
|
|
||||||
|
### Server + Desktop (default)
|
||||||
|
|
||||||
|
All services are enabled: Bitcoin, Matrix Synapse, Vaultwarden, WordPress, Nextcloud.
|
||||||
|
|
||||||
|
| Stage | Status | Notes |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| Stage 1 — NixOS config | ✅ Backed up | Full server configuration |
|
||||||
|
| Stage 2 — Secrets | ✅ Backed up | Bitcoin secrets, domain configs, and Hub state |
|
||||||
|
| Stage 3 — Home directory | ✅ Backed up | Desktop user data |
|
||||||
|
| Stage 4 — LND wallet | ✅ Backed up | Lightning wallet and channel data |
|
||||||
|
|
||||||
|
This produces the largest backup. All four stages generate meaningful data.
|
||||||
|
|
||||||
|
### Desktop Only
|
||||||
|
|
||||||
|
All server services are disabled (`bitcoin = false`, `synapse = false`, `vaultwarden = false`, `wordpress = false`, `nextcloud = false`). Only GNOME desktop is active.
|
||||||
|
|
||||||
|
| Stage | Status | Notes |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| Stage 1 — NixOS config | ✅ Backed up | Simpler config (no server services) |
|
||||||
|
| Stage 2 — Secrets | ⚠️ Partial | `/etc/nix-bitcoin-secrets` is **skipped** (not applicable for Desktop Only role). `/var/lib/domains` and `/var/lib/secrets` (Hub state) are still backed up if present |
|
||||||
|
| Stage 3 — Home directory | ✅ Backed up | **The most important data for this role** |
|
||||||
|
| Stage 4 — LND wallet | ⏭️ Skipped | Explicitly skipped — not applicable for Desktop Only role |
|
||||||
|
|
||||||
|
This produces the smallest and fastest backup. Stages 1 and 3 are the primary sources of meaningful data.
|
||||||
|
|
||||||
|
### Node (Bitcoin-only)
|
||||||
|
|
||||||
|
Only the Bitcoin ecosystem is active: `bitcoind`, `electrs`, `lnd`, `rtl`, `btcpay`, `mempool`, and `bip110`. All other server services are disabled.
|
||||||
|
|
||||||
|
| Stage | Status | Notes |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| Stage 1 — NixOS config | ✅ Backed up | Node-specific configuration |
|
||||||
|
| Stage 2 — Secrets | ✅ Backed up | Bitcoin secrets and Hub state. `/var/lib/domains` may be minimal (BTCPay runs but is not exposed via Caddy) |
|
||||||
|
| Stage 3 — Home directory | ✅ Backed up | User data |
|
||||||
|
| Stage 4 — LND wallet | ✅ Backed up | **Critical** — Lightning wallet and channel data |
|
||||||
|
|
||||||
|
All four stages run, matching Server + Desktop behaviour. The `/var/lib/domains` directory may be sparsely populated since non-Bitcoin web services are not configured.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backup Manifest
|
||||||
|
|
||||||
|
After all stages complete, the script writes a `BACKUP_MANIFEST.txt` file inside the timestamped backup directory. This file records the date, hostname, detected role, target drive, and a directory listing of everything that was backed up.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Running the Backup
|
||||||
|
|
||||||
|
The backup is triggered from the Sovran Hub web UI. You can also run it directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Auto-detect the first external USB drive
|
||||||
|
sudo bash /path/to/sovran-hub-backup.sh
|
||||||
|
|
||||||
|
# Specify a target drive explicitly
|
||||||
|
sudo BACKUP_TARGET=/run/media/<user>/<drive> bash /path/to/sovran-hub-backup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The script requires at least **10 GB** of free space on the target drive and will refuse to write to internal system drives.
|
||||||
|
|
||||||
|
Logs are written to `/var/log/sovran-hub-backup.log` and the current status (`RUNNING`, `SUCCESS`, or `FAILED`) is tracked in `/var/log/sovran-hub-backup.status`.
|
||||||
Reference in New Issue
Block a user