Files
esh-pfi-infrastructure/docs/runbooks/pbs-deployment.md
T

737 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Proxmox Backup Server deployment runbook
Stand up PBS-ANA as the fleet's primary vzdump target, then PBS-NH3 as
a DR mirror that pulls nightly from PBS-ANA. Migrate all 5 hypervisors
(pfi-pve, nh3-pve, esh-pve, esh-pve-nas, sfsrv-ana) off their current
local-dump vzdump targets onto PBS-ANA.
Architecture decision + rationale: see `STATUS.md` and the earlier
session transcript. Short version: one primary at ANA, one-way sync to
NH3, ANA owns retention policy.
**Estimated total time: 46 hours spread across sittings.** Each phase
below is a self-contained work unit with a clear done-state; you can
stop after any phase without leaving the fleet in a bad state.
## Phase 0 — Prerequisites (one-time, before Phase 1)
### 0.1. Check current NFS exports
```bash
ssh -t 10.250.50.50 'sudo exportfs -v'
```
The existing broad exports (e.g. `/mnt/backup``10.0.0.0/8`) cover
pfi-pve but use `root_squash` — which will **block** PBS from writing
its datastore metadata. We need a dedicated, no-squash export scoped
to the PBS-ANA VM's IP.
### 0.2. Create a dedicated export for PBS-ANA
Assign PBS-ANA its LAN IP (let's say `10.250.50.90` — Phase 1.1 uses
this), then on the Debian NAS:
```bash
ssh -t 10.250.50.50 '
sudo mkdir -p /mnt/backup/pbs-ana &&
sudo chown root:root /mnt/backup/pbs-ana &&
sudo chmod 755 /mnt/backup/pbs-ana &&
echo "/mnt/backup/pbs-ana 10.250.50.90(rw,sync,no_root_squash,no_subtree_check,sec=sys)" \
| sudo tee /etc/exports.d/pbs-ana.exports > /dev/null &&
sudo exportfs -ra &&
sudo exportfs -v | grep pbs-ana
'
```
Should print an `/mnt/backup/pbs-ana` line with `no_root_squash`.
The existing `cockpit-file-sharing.exports` is managed by Cockpit's
File Sharing module; keep the PBS-specific export in a separate file
so Cockpit doesn't clobber it.
### 0.3. Decide datastore paths
- **PBS-ANA datastore path:** `/mnt/backup/pbs-ana/` on the NAS, mounted
at `/mnt/pbs-datastore/` inside the PBS-ANA VM (Phase 1.3).
- **PBS-NH3 datastore path:** local-only on nh3-pve, mounted at
`/mnt/pbs-datastore/` inside the PBS-NH3 VM (Phase 5.3).
### 0.4. Side-note: `/mnt/pve-VMStorage` is exported to `<world>`
Not blocking PBS work but worth tightening during a Cockpit pass —
reduce to `10.0.0.0/8` or more specific. Open item, not blocker.
### 0.5. ZFS case-insensitivity breaks NFSv4 writes
The Debian NAS's `/mnt/backup` is a ZFS dataset with
`casesensitivity=insensitive`. ZFS + NFSv4 + case-insensitive dataset
is a known-bad combo: lookups/readdir work, `create`/`write` ops fail
with EACCES even for root with `no_root_squash`.
**Workaround:** mount with NFSv3 instead of NFSv4. Update the VM's
`/etc/fstab` filesystem type to `nfs` and options to `vers=3`. PBS's
access pattern (dedupe chunks are writen-once/read-many) works fine
over NFSv3.
Example `/etc/fstab` line that works:
```
10.250.50.50:/mnt/backup/pbs-ana /mnt/pbs-datastore nfs defaults,_netdev,bg,hard,timeo=600,retrans=2,vers=3 0 0
```
**Cleaner long-term fix** (not required, but nicer): create a
case-sensitive child dataset for PBS's data:
```
sudo zfs create -o casesensitivity=sensitive -o mountpoint=/mnt/backup/pbs-ds \
NASPool/backupStore/pbs-ds
```
Then export/mount `/mnt/backup/pbs-ds` via NFSv4 as originally planned.
Case-sensitivity can't be changed on an existing dataset, which is why
the parent is stuck on case-insensitive.
## Phase 1 — Provision PBS-ANA (VM on pfi-pve)
**Target state:** a fresh Debian 12 VM on pfi-pve running
`proxmox-backup-server`, with a datastore configured on the NFS-mounted
NAS path. Reachable at `pbs-ana.phasefinal.com` (or by IP).
### 1.1. Create a Debian 12 VM on pfi-pve
Via the pve web UI or CLI. Starting specs:
| Setting | Value |
|---|---|
| Name | `pbs-ana` |
| VMID | next available (probably 114 given pfi-pve's fleet) |
| ISO | debian-12.x.x-amd64-netinst |
| CPU | 4 cores |
| RAM | 8 GB (can be tight at scale; bump if needed) |
| Disk | 32 GB on `local-lvm` (for OS only — datastore is NFS) |
| Network | vmbr0, firewall on |
| Onboot | yes |
Install Debian 12 minimal (no desktop, SSH server, standard system utilities).
Once up, set a static IP in the `servers/` LAN range (e.g.
`10.250.50.90`) or DHCP-reserved for stability.
### 1.2. Install PBS package
SSH in as root:
```bash
# Add the PBS no-subscription repo
cat > /etc/apt/sources.list.d/pbs-no-subscription.list <<'EOF'
deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription
EOF
wget https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg \
-O /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
apt update
apt full-upgrade -y
apt install -y proxmox-backup-server
```
Reboot once the PBS kernel is installed (PBS kernel is Proxmox's
patched one, like PVE hosts):
```bash
systemctl reboot
```
After reboot, verify the web UI is reachable:
```
https://10.250.50.90:8007/
```
Default login: `root` / (the Debian root password you set at install).
### 1.3. Mount NAS datastore path
On the PBS VM:
```bash
# Create mount point
mkdir -p /mnt/pbs-datastore
# Add persistent NFS mount
cat >> /etc/fstab <<'EOF'
10.250.50.50:/mnt/backup/pbs-ana /mnt/pbs-datastore nfs4 defaults,_netdev,bg,hard,timeo=600,retrans=2,vers=4.2 0 0
EOF
# Create the dir on NAS side if it doesn't exist
ssh ana-nas 'mkdir -p /mnt/backup/pbs-ana && chown root:root /mnt/backup/pbs-ana'
# Mount
mount -a
mount | grep pbs-datastore
# Should show the NFS mount active
df -h /mnt/pbs-datastore
```
### 1.4. Create PBS datastore
In the PBS web UI: **Datastore → Add Datastore**:
| Field | Value |
|---|---|
| Name | `backups` |
| Backing Path | `/mnt/pbs-datastore` |
| Retention (can set later) | — |
| GC Schedule | Sunday 06:00 |
| Prune Schedule | Daily 05:30 |
The first time PBS writes to the datastore it initializes `.chunks/`,
`.lock`, etc. Takes a few seconds.
### 1.4b. Create per-hypervisor namespaces
VMIDs are NOT globally unique across PVE hosts (esh-pve and sfsrv-ana
both have VM 100 for instance). Without namespaces, their backups
collide under the same `/vm/100/` path in the datastore. Create a
namespace per hypervisor up front:
`proxmox-backup-manager` doesn't manage namespaces — they're created
through the API/web UI or the `proxmox-backup-client` tool.
**Easiest: web UI.** Datastore → `backups` → Content → top of pane
there's a namespace selector with an **Add NS** button. Add one per
hypervisor.
**Scripted via `proxmox-backup-client`** (run on PBS-ANA):
```bash
export PBS_REPOSITORY='root@pam@localhost:backups'
export PBS_PASSWORD='<root-pam-password>'
for ns in pfi-pve nh3-pve esh-pve esh-pve-nas sfsrv-ana; do
proxmox-backup-client namespace create "$ns"
done
proxmox-backup-client namespace list
```
**Scripted via API** (when ssh access is more convenient than shell
on PBS-ANA):
```bash
TOKEN='<fleet-vzdump-secret>'
for ns in pfi-pve nh3-pve esh-pve esh-pve-nas sfsrv-ana; do
curl -sk \
-H "Authorization: PBSAPIToken=root@pam!fleet-vzdump:$TOKEN" \
-X POST \
https://10.250.50.90:8007/api2/json/admin/datastore/backups/namespace \
-d "{\"ns\":\"$ns\"}"
done
```
Each PVE client later (Phase 2.1, 3.1, 4) sets its Namespace field to
its own hostname when configuring the PBS storage.
### 1.4c. Create a verify job
New snapshots land unverified — PBS treats "backup completed" and
"backup verified" as separate states. A verify job hash-checks chunk
data (not just the manifest), flips snapshots to verified, and catches
bitrot on aging data.
Web UI: **Datastore → backups → Verify Jobs → Add**:
| Field | Value |
|---|---|
| Schedule | `sat 23:00` |
| Ignore verified snapshots | ✓ |
| Re-verify after (days) | `30` |
| Max depth | blank (unlimited) |
| Namespace | blank (root — recurses into all namespaces) |
| Comment | `fleet verify — new + 30d re-check` |
Rationale for these defaults:
- **`sat 23:00`** — sits between the daily 03:00 backup window and the
Sunday 06:00 GC run, so verify never fights GC for I/O, and every
week's new backups get verified before pruning decisions happen.
- **`ignore-verified: true`** + **`outdated-after: 30d`** — efficient
steady state. First run after a backup night does the new snapshots
only; a 30-day rolling re-verify catches silent chunk corruption.
- **unlimited depth, root namespace** — one job covers all 5
hypervisor namespaces. Split into per-namespace jobs only if you
want per-hypervisor visibility into verify failures (not necessary
for a fleet this size).
Verify runs are I/O-heavy on the datastore — on the NFS-backed
PBS-ANA, expect a full-datastore verify to take hours once the
datastore grows. The `ignore-verified` flag keeps incremental verify
cheap; only the 30-day-aged portion is re-read each run.
### 1.5. Create an API token for hypervisors to use
PBS web UI: **Configuration → Access Control → API Token → Add**:
| Field | Value |
|---|---|
| User | `root@pam` |
| Token Name | `fleet-vzdump` |
| Privilege Separation | **off** (so the token inherits root perms) |
Save the token secret — this is the only time PBS shows it. Store in
your password manager alongside the other fleet creds.
Token identifier looks like: `root@pam!fleet-vzdump`
Secret looks like: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
### 1.6. Note PBS fingerprint
For client connections:
```bash
# On PBS VM
proxmox-backup-manager cert info | grep -i fingerprint
```
Save this fingerprint — each PVE client needs it to verify PBS's cert.
### Phase 1 done-state
- PBS-ANA reachable at `https://<ip>:8007/`
- Datastore `backups` created and healthy
- API token `root@pam!fleet-vzdump` issued
- Cert fingerprint recorded
## Phase 2 — Migrate pfi-pve's vzdump jobs onto PBS-ANA
**Target state:** pfi-pve sends its nightly backups to PBS-ANA.
### 2.1. Add PBS-ANA as storage on pfi-pve
Web UI: **Datacenter → Storage → Add → Proxmox Backup Server**:
| Field | Value |
|---|---|
| ID | `pbs-ana` |
| Server | PBS-ANA IP |
| Username | `root@pam!fleet-vzdump` |
| Password | the token secret from 1.5 |
| Datastore | `backups` |
| **Namespace** | **`pfi-pve`** (match the hypervisor name — see 1.4b) |
| Fingerprint | from 1.6 |
| Content | VZDump backup file |
| Nodes | `pve` (pfi-pve) |
Click Add. Storage should show as available within seconds.
> **Critical:** every hypervisor gets its own namespace value matching
> its own name — `nh3-pve`, `esh-pve`, `esh-pve-nas`, `sfsrv-ana`.
> Prevents VMID collisions (not-globally-unique VMIDs would otherwise
> land in the same `/vm/<vmid>/` path).
### 2.2. Test backup of one VM
Pick a small VM (e.g. an idle LXC like ana-filebot, VMID 112). Web UI:
**select VM → Backup → Backup Now → Storage: pbs-ana**.
First backup is a full dump. For a small LXC, a few minutes. Verify:
- Backup completes with green OK status
- In PBS UI: **Datastore → backups** shows the snapshot under
`ct/112/` or `vm/112/`
### 2.3. Migrate the scheduled backup job
Web UI: **Datacenter → Backup**. Edit the existing pfi-pve backup job
(`backup-4b911ca7-92df` from the audit).
| Change | From | To |
|---|---|---|
| Storage | `naspool-vmstorage` (was `pve-truenas` until 2026-09-06) | `pbs-ana` |
| Mode | snapshot | snapshot |
| Compression | zstd | zstd |
Save. Next scheduled run (03:00 by default) fires against PBS-ANA.
### 2.4. Run it manually to verify
Web UI: **backup job → Run Now**. Watch each VM's backup complete.
Larger VMs (PFI-Postgres 8GB RAM, PFI-Mongo, etc.) take 10-30 minutes
each on first full backup. Subsequent backups will be minutes due to
dirty-bitmap incrementals.
### 2.5. Keep legacy target for overlap
**The old `pve-truenas` storage id was renamed `naspool-vmstorage` on 2026-09-06** (same path, `/NASPool/pve-VMStorage`, on the rebuilt raidz2 pool; legacy vzdump pruned to newest-per-guest). Historical note follows. Wait ~1 week
of successful PBS runs before retiring it. Double-coverage period is
cheap insurance.
### Phase 2 done-state
- pfi-pve's 11 guests have first PBS snapshots
- Scheduled job uses PBS-ANA
- Old `pve-truenas` target (now `naspool-vmstorage`) still exists but no new jobs
write to it
## Phase 3 — Onboard esh-pve, esh-pve-nas, sfsrv-ana
**Target state:** all three remaining ANA/ESH hypervisors use PBS-ANA.
### 3.1. Repeat Phase 2.1 on each hypervisor
On each of `esh-pve`, `esh-pve-nas`, `sfsrv-ana`:
- Web UI: Datacenter → Storage → Add → Proxmox Backup Server (same
fields as Phase 2.1; same token is fine — it's fleet-wide).
### 3.2. Configure vzdump jobs
`esh-pve` and `esh-pve-nas` already have vzdump jobs hitting local
dump dirs. Edit them per Phase 2.3.
`sfsrv-ana` has **no existing vzdump jobs** (per audit). Create one:
Web UI: Datacenter → Backup → Add:
| Field | Value |
|---|---|
| Storage | `pbs-ana` |
| Schedule | `03:00` (daily) |
| Selection | All |
| Retention | keep-daily 7, weekly 4, monthly 12, yearly 3 |
| Compression | zstd |
| Mode | snapshot |
| Email | your address, notifications on failure |
### 3.3. First backups
Fire manual runs from each hypervisor. ESH site backups cross-WAN to
ANA — first full dumps will take a while. Schedule overnight if
bandwidth is an issue during the day.
### 3.4. Verify on PBS-ANA side
Web UI on PBS: **Datastore → backups → Content**. Should show
namespaces / groups for guests from all 4 hypervisors by hostname-ish
prefixes (PBS uses VMID + node).
### Phase 3 done-state
- All 3 hypervisors have working PBS backups
- sfsrv-ana specifically goes from ZERO backup coverage to full
vzdump coverage (biggest single gain of this migration)
## Phase 4 — Onboard nh3-pve
**Target state:** nh3-pve uses PBS-ANA as backup target despite the
WAN hop.
Same process as Phase 3. The only extra considerations:
- **Bandwidth:** first full backups of NH3 VMs cross the WAN to ANA.
Estimate size and run during off-hours. Incrementals after that
should be small.
- **Verify WAN stability:** if the link flaps during a backup, PBS
retries automatically but a multi-hour drop will fail. Don't start
a 50 GB backup during known-unstable windows.
### Phase 4 done-state
- All 5 hypervisors back up to PBS-ANA
- Verified: ~24 guests total across the fleet, all with recent
PBS snapshots
## Phase 5 — Provision PBS-NH3 (DR mirror)
**Target state:** PBS-NH3 running on nh3-pve, ready to pull from
PBS-ANA.
### 5.1. Create Debian VM on nh3-pve
Same pattern as Phase 1.1:
| Setting | Value |
|---|---|
| Name | `pbs-nh3` |
| VMID | next available on nh3-pve |
| CPU | 2 cores |
| RAM | 4 GB |
| Disk | 32 GB OS + data disk (see 5.3) |
| Network | vmbr0 |
### 5.2. Install PBS
Same as Phase 1.2.
### 5.3. Datastore backing — Synology NFS (chosen 2026-04-22)
PBS-NH3 mounts a Synology NFS share rather than using a local virtual
disk. Simpler storage admin; tradeoff is that PBS-NH3 now shares its
failure domain with the other NH3 backup paths (nh3-docker restic,
nh3-dev restic, Backrest repos). Acceptable for DR purposes because
PBS-ANA remains primary.
**Synology-side export setup (DSM):**
1. Create a dedicated share, e.g. `pbs-nh3`, on the target volume.
2. **Control Panel → Shared Folder → [share] → Edit → NFS Permissions → Add/Edit:**
| Field | Value |
|---|---|
| Hostname/IP | PBS-NH3 VM IP |
| Privilege | Read/Write |
| Squash | **No mapping** (Synology's label for `no_root_squash`) |
| Security | sys |
| Enable asynchronous | on |
| Allow connections from non-privileged ports | on |
3. Verify no **Advanced Permissions** ACL denies `root` write — those
override NFS perms and cause silent write failures.
**Client-side mount (on PBS-NH3 VM):**
Use **NFSv3**, not NFSv4 — see the gotcha below. Synology's "Advanced
Permissions" layer an NFSv4 ACL on top of POSIX mode that is invisible
to `ls` but denies writes to unprivileged users (including the
`backup` uid-34 that PBS runs as), even when the directory mode is
777. Root bypasses this via `no_root_squash`, which is why a root
`touch` succeeds but the datastore init fails.
```bash
mkdir -p /mnt/pbs-datastore
cat >> /etc/fstab <<'EOF'
10.100.50.50:/volume1/pbs-nh3 /mnt/pbs-datastore nfs defaults,_netdev,bg,hard,timeo=600,retrans=2,vers=3 0 0
EOF
mount -a
df -h /mnt/pbs-datastore
# Two-layer smoke test: root AND the backup user that PBS runs as.
# The backup-user check is the one that actually matters — if root
# works but backup doesn't, you're hitting the ACL override.
touch /mnt/pbs-datastore/root-write-test && rm /mnt/pbs-datastore/root-write-test
sudo -u backup touch /mnt/pbs-datastore/backup-user-test && rm /mnt/pbs-datastore/backup-user-test
```
Ensure NFSv3 is reachable on the Synology side: **Control Panel →
File Services → NFS → Advanced** — confirm "Minimum NFS Protocol" is
3 (not 4.0+). Default is usually 3; only an issue if someone
hardened it previously.
**Fallback if NFSv3 still fails the backup-user check:** the deny is
a Synology-local syno_acl (`ls -la` on the Synology shows POSIX mode
`d---------+` with a `+` for extended ACL). DSM "Enable Advanced
Permissions" unchecked does NOT reset this once the archive flag
`has_ACL,is_support_ACL` is set on the share.
Diagnose from the Synology shell:
```
sudo synoacltool -get /volume1/<share>
sudo ls -la /volume1/<share>/
```
A `+` after the permission string + a `group:administrators:allow:...`
ACL entry + POSIX `d---------` is the smoking gun: only members of
the `administrators` group have access, which is why root (with
`no_root_squash`) writes but uid-34 (backup) doesn't.
**Fix (confirmed working 2026-04-22):** keep **Squash: `No mapping`**
(i.e. `no_root_squash`) AND flatten the share to Linux/POSIX mode
with `chmod 777`. This drops the syno_acl entirely — verifiable by
`synoacltool -get` returning "It's Linux mode" and `ls -la`
showing `drwxrwxrwx` with NO trailing `+`.
On the Synology shell:
```bash
sudo chmod 777 /volume1/<share>
# Verify pure POSIX, no ACL
sudo synoacltool -get /volume1/<share> # should say "It's Linux mode"
ls -la /volume1/<share>/ # should show drwxrwxrwx (no '+')
```
Pure POSIX 777 is a cleaner long-term config than the ACL-grant
approach — fewer permission-translation layers between NFSv3 and
Btrfs, and no chance of ACL inheritance surprises on PBS-created
subdirectories.
**Why `all_squash` alone doesn't work:** it lets backup-user writes
through (because the ACL-granted admin gets the mapped uid), but
breaks PBS's `chown()` during init. Squashed-admin doesn't have
CAP_CHOWN on the Synology side → EPERM. Only real root (via
`no_root_squash`) can chown to uid 34.
**Why squash alone fails:** even with `all_squash + anonuid=1024`
letting backup-user writes succeed (because admin is in
`administrators` ACL), PBS's datastore-init calls `chown` on
newly-created paths. Squashed-admin doesn't have CAP_CHOWN on the
Synology side → EPERM. Only a real root (via `no_root_squash`) can
chown to uid 34.
**Smoke-test all three paths after the fix:**
```bash
sudo touch /mnt/pbs-datastore/t && rm /mnt/pbs-datastore/t && echo ROOT_OK
sudo -u backup touch /mnt/pbs-datastore/t && rm /mnt/pbs-datastore/t && echo BACKUP_OK
touch /mnt/pbs-datastore/t && chown 34:34 /mnt/pbs-datastore/t && rm /mnt/pbs-datastore/t && echo CHOWN_OK
```
All three must pass before PBS will init cleanly.
**Historical note:** both PBS instances ended up on NFSv3 for
unrelated reasons — the ANA side because of ZFS case-insensitivity,
the NH3 side because of Synology ACL override. NFSv3 is the safer
default for PBS-on-NFS regardless of backend.
### 5.4. Create datastore
Web UI: Datastore → Add, name `backups-mirror`, path `/mnt/pbs-datastore`.
### 5.5. Create a verify job on the mirror
Same pattern as Phase 1.4c on PBS-ANA — verification doesn't replicate
across PBS instances, so the mirror needs its own job to catch bitrot
on the local datastore disk.
Web UI: **Datastore → backups-mirror → Verify Jobs → Add**:
| Field | Value |
|---|---|
| Schedule | `sun 12:00` |
| Ignore verified snapshots | ✓ |
| Re-verify after (days) | `30` |
| Max depth | blank (unlimited) |
| Namespace | blank |
| Comment | `mirror verify — catches DR-side bitrot` |
Schedule sits after the 06:00 sync job finishes, so newly-synced
snapshots get verified same day.
### Phase 5 done-state
- PBS-NH3 reachable, datastore ready
- No sync job yet — that's Phase 6
## Phase 6 — Sync PBS-ANA → PBS-NH3
**Target state:** PBS-NH3 nightly pulls everything from PBS-ANA.
### 6.1. Create a remote entry on PBS-NH3
PBS-NH3 web UI: **Configuration → Remotes → Add**:
| Field | Value |
|---|---|
| Remote ID | `pbs-ana` |
| Host | PBS-ANA IP |
| User ID | `root@pam!fleet-vzdump` (or a dedicated read-only token — see 6.3) |
| Password | token secret |
| Fingerprint | PBS-ANA's cert fingerprint |
### 6.2. Create a sync job
Web UI: **Datastore → backups-mirror → Sync Jobs → Add**:
| Field | Value |
|---|---|
| Remote | `pbs-ana` |
| Remote Datastore | `backups` |
| Sync Direction | Pull |
| Schedule | `*-*-* 06:00:00` (after primary backups at 03:00) |
| Max Depth | unlimited |
| Owner | `root@pam` |
Save. Run once manually to confirm.
First run transfers everything the primary has. This is one-time heavy
— schedule on a weekend if bandwidth is a concern.
### 6.3. (Optional) tighten the token
The `fleet-vzdump` token has write permissions on PBS-ANA. For
sync-only access, create a second read-only token on PBS-ANA
(`Datastore.Audit` + `Remote.Audit`) and use that on the PBS-NH3
remote config. Reduces blast radius if PBS-NH3 ever compromised.
### 6.4. Set retention on PBS-NH3
Shorter than primary (it's DR, not archival):
Web UI: Datastore → backups-mirror → Prune & GC → Edit:
| Field | Value |
|---|---|
| keep-last | 7 |
| keep-daily | 7 |
| keep-weekly | 4 |
### Phase 6 done-state
- Nightly pull from PBS-ANA → PBS-NH3 runs cleanly
- PBS-NH3 holds last 7 days of backups (shorter retention, DR focus)
## Phase 7 — Burn-in
**Target state:** ~1 week of clean operation before retiring legacy targets.
Daily checks:
```bash
# PBS-ANA datastore growth is sensible
ssh root@pbs-ana 'df -h /mnt/pbs-datastore'
# PBS-NH3 sync job succeeded overnight
ssh root@pbs-nh3 'journalctl -u proxmox-backup-proxy --since yesterday | grep -i sync'
# Every hypervisor's last backup is ≤24h old
# (Run refresh-proxmox-info.sh and inspect the backup-coverage section)
scripts/refresh-proxmox-info.sh all
```
Watch for:
- Failed backup jobs (email alerts if enabled)
- Sync job duration growing unexpectedly (bandwidth issue)
- Datastore approaching capacity (bump retention or storage)
- VMs that don't dirty-bitmap incremental (missing QEMU Guest Agent,
shut-down VMs always do full dumps)
## Phase 8 — Retire legacy vzdump targets
**Target state:** no more old-style local-dump backups running; just PBS.
Only after Phase 7 passes:
- On each hypervisor: remove old vzdump jobs that targeted local dump
dirs.
- Optionally delete old VMA files from local dump storage to reclaim
space (before deleting, verify PBS has the same vintage).
- Update `servers/*-pve/proxmox-details.txt` by re-running
`scripts/refresh-proxmox-info.sh all` — the `backup coverage` section
should now show every guest covered by the PBS-ANA job.
## Phase 9 — Update this workspace
- Update `STATUS.md` coverage table to show PBS deployment state.
- Update memory `project_backup_pipeline_gaps.md` — PBS closes several
of the open gaps.
- Update `README.md` backup pipeline section to describe the new
topology.
- Add an entry for PBS-ANA and PBS-NH3 in `servers/` (with README +
ssh-target). They're first-class infrastructure now.
- Homepage Infra-ANA card pointing at PBS-ANA Web UI; Infra-NH3 for
PBS-NH3.
## Rollback posture
At any phase, you can stop and leave the fleet in its current state:
- Phase 1 incomplete: no impact, PBS-ANA just sits idle.
- Phase 2+ incomplete: affected hypervisors still have their old
vzdump jobs running. Until Phase 8, old targets are kept as
redundant. Rolling back one hypervisor means deleting its
PBS storage entry and re-enabling its old job.
- Phase 6+ incomplete: PBS-ANA still provides primary backup coverage.
DR mirror just isn't built yet.