|
| 1 | +# Runbook: PBS Backup Maintenance |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Proxmox Backup Server (PBS) requires three scheduled maintenance tasks to prevent disk exhaustion: **prune**, **garbage collection (GC)**, and **verify**. Without these, the backup datastore grows unbounded and can crash VMs during backup (see RCA: `pbs-disk-full-vm-crash-rca.md`). |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +``` |
| 10 | +┌─────────────────────────────────────────────────────────────────────┐ |
| 11 | +│ PBS Maintenance Pipeline (Daily) │ |
| 12 | +├─────────────────────────────────────────────────────────────────────┤ |
| 13 | +│ │ |
| 14 | +│ 22:30 vzdump backup runs (PVE cluster job) │ |
| 15 | +│ │ └─ Backs up all VMs/CTs except 103,108 │ |
| 16 | +│ ▼ │ |
| 17 | +│ 04:00 Prune job (PBS) │ |
| 18 | +│ │ └─ Marks old snapshots for removal (keep-daily=3, weekly=2) │ |
| 19 | +│ ▼ │ |
| 20 | +│ 05:00 Garbage collection (PBS) │ |
| 21 | +│ │ └─ Deletes unreferenced chunks, reclaims disk space │ |
| 22 | +│ ▼ │ |
| 23 | +│ sat 03:00 Verify job (PBS, weekly) │ |
| 24 | +│ └─ Validates backup integrity, checks for bit rot │ |
| 25 | +│ │ |
| 26 | +│ PBS Container: LXC 103 on pumped-piglet.maas │ |
| 27 | +│ Datastore: homelab-backup at /mnt/homelab-backup │ |
| 28 | +│ Storage: ZFS pool local-3TB-backup │ |
| 29 | +│ │ |
| 30 | +└─────────────────────────────────────────────────────────────────────┘ |
| 31 | +``` |
| 32 | + |
| 33 | +## Configuration Reference |
| 34 | + |
| 35 | +| Setting | Value | Location | |
| 36 | +|---------|-------|----------| |
| 37 | +| PBS container | LXC 103 on pumped-piglet | `qm list` | |
| 38 | +| PBS IP | 192.168.4.211 | `/etc/pve/storage.cfg` | |
| 39 | +| PBS Web UI | `https://proxmox-backup-server.maas:8007` | Browser | |
| 40 | +| Datastore | `homelab-backup` at `/mnt/homelab-backup` | PBS config | |
| 41 | +| ZFS pool | `local-3TB-backup` | pumped-piglet host | |
| 42 | +| Backup schedule | 2nd and 22nd of month at 22:30 | PVE cluster job | |
| 43 | +| Backup exclusions | VM 103 (PBS itself), VM 108 (still-fawn K3s) | PVE cluster job | |
| 44 | +| Retention (vzdump) | keep-daily=3, keep-weekly=2 | PVE cluster job | |
| 45 | +| Retention (PBS prune) | keep-daily=3, keep-weekly=2 | PBS prune job | |
| 46 | +| GC schedule | Daily at 05:00 | PBS datastore config | |
| 47 | +| Verify schedule | Saturday at 03:00 | PBS verify job | |
| 48 | +| Age key (SOPS) | `~/.config/sops/age/keys.txt` | Local Mac | |
| 49 | + |
| 50 | +## Common Operations |
| 51 | + |
| 52 | +### Check Datastore Health |
| 53 | + |
| 54 | +```bash |
| 55 | +# Pool free space (should be >500 GB) |
| 56 | +ssh root@pumped-piglet.maas "zfs list -o name,used,avail local-3TB-backup" |
| 57 | + |
| 58 | +# GC status (last run, bytes removed) |
| 59 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager garbage-collection status homelab-backup" |
| 60 | + |
| 61 | +# List scheduled jobs |
| 62 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager prune list" |
| 63 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager verify-job list" |
| 64 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager datastore show homelab-backup" |
| 65 | + |
| 66 | +# Backup inventory (all snapshots) |
| 67 | +ssh root@pumped-piglet.maas "pvesm list homelab-backup" |
| 68 | + |
| 69 | +# Dedup stats |
| 70 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager garbage-collection status homelab-backup" |
| 71 | +# Look for: Deduplication factor, On-Disk usage |
| 72 | +``` |
| 73 | + |
| 74 | +### Manual Prune |
| 75 | + |
| 76 | +```bash |
| 77 | +# Run prune job immediately |
| 78 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager prune run homelab-prune" |
| 79 | +``` |
| 80 | + |
| 81 | +### Manual Garbage Collection |
| 82 | + |
| 83 | +```bash |
| 84 | +# Run GC immediately (can take 10-30 min on large datastores) |
| 85 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager garbage-collection start homelab-backup" |
| 86 | + |
| 87 | +# NOTE: GC will fail with ENOSPC if the pool is 100% full |
| 88 | +# In that case, free space at the ZFS level first (see Emergency section) |
| 89 | +``` |
| 90 | + |
| 91 | +### Manual Verify |
| 92 | + |
| 93 | +```bash |
| 94 | +# Run verify immediately |
| 95 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager verify-job run homelab-verify" |
| 96 | +``` |
| 97 | + |
| 98 | +### Check Backup Job Status |
| 99 | + |
| 100 | +```bash |
| 101 | +# PVE cluster backup job config |
| 102 | +ssh root@pumped-piglet.maas "pvesh get /cluster/backup --output-format json | jq '.[0]'" |
| 103 | + |
| 104 | +# Recent backup task logs |
| 105 | +ssh root@pumped-piglet.maas "journalctl | grep -iE 'vzdump|backup' | tail -20" |
| 106 | +``` |
| 107 | + |
| 108 | +## Emergency: Pool Full |
| 109 | + |
| 110 | +If `local-3TB-backup` hits 100%, follow this order: |
| 111 | + |
| 112 | +### 1. Free ZFS-Level Space First |
| 113 | + |
| 114 | +GC cannot run on a full pool (needs space to update atimes). Free space at the ZFS level: |
| 115 | + |
| 116 | +```bash |
| 117 | +# Check for stale vzdump temp directories |
| 118 | +ssh root@pumped-piglet.maas "du -sh /local-3TB-backup/backup-tmpdir/*" |
| 119 | +# Delete if stale (no active backup running): |
| 120 | +ssh root@pumped-piglet.maas "rm -rf /local-3TB-backup/backup-tmpdir/vzdumptmp*" |
| 121 | + |
| 122 | +# Check for old ZFS snapshots |
| 123 | +ssh root@pumped-piglet.maas "zfs list -t snapshot -o name,used,creation -r local-3TB-backup" |
| 124 | +# Destroy old snapshots: |
| 125 | +ssh root@pumped-piglet.maas "zfs destroy local-3TB-backup/DATASET@SNAPSHOT_NAME" |
| 126 | + |
| 127 | +# Verify space freed |
| 128 | +ssh root@pumped-piglet.maas "zfs list -o name,avail local-3TB-backup" |
| 129 | +``` |
| 130 | + |
| 131 | +### 2. Run Prune Then GC |
| 132 | + |
| 133 | +```bash |
| 134 | +# Prune first (marks snapshots for removal) |
| 135 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager prune run homelab-prune" |
| 136 | + |
| 137 | +# Then GC (actually deletes chunks and reclaims space) |
| 138 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager garbage-collection start homelab-backup" |
| 139 | +``` |
| 140 | + |
| 141 | +### 3. If a VM Crashed |
| 142 | + |
| 143 | +If a VM is stopped due to a frozen filesystem from a failed backup: |
| 144 | + |
| 145 | +```bash |
| 146 | +# Check VM status |
| 147 | +ssh root@pumped-piglet.maas "qm status <VMID>" |
| 148 | + |
| 149 | +# Start the VM (only after freeing disk space) |
| 150 | +ssh root@pumped-piglet.maas "qm start <VMID>" |
| 151 | + |
| 152 | +# If start fails, check for lock files |
| 153 | +ssh root@pumped-piglet.maas "ls -la /var/lock/qemu-server/lock-<VMID>.conf" |
| 154 | +ssh root@pumped-piglet.maas "rm /var/lock/qemu-server/lock-<VMID>.conf" |
| 155 | +``` |
| 156 | + |
| 157 | +## Modifying Retention Policy |
| 158 | + |
| 159 | +### PBS Prune Job |
| 160 | + |
| 161 | +```bash |
| 162 | +# Update retention (e.g., keep more daily backups) |
| 163 | +ssh root@pumped-piglet.maas "pct exec 103 -- proxmox-backup-manager prune update homelab-prune \ |
| 164 | + --keep-daily 7 --keep-weekly 4 --keep-monthly 3" |
| 165 | +``` |
| 166 | + |
| 167 | +### PVE Cluster Backup Job |
| 168 | + |
| 169 | +```bash |
| 170 | +# Update via pvesh (match PBS prune retention) |
| 171 | +ssh root@pumped-piglet.maas "pvesh set /cluster/backup/backup-ff3d789f-f52b \ |
| 172 | + --prune-backups keep-daily=7,keep-weekly=4,keep-monthly=3" |
| 173 | +``` |
| 174 | + |
| 175 | +### GitOps (pbs-storage.yaml) |
| 176 | + |
| 177 | +The desired state is in `proxmox/homelab/config/pbs-storage.yaml`. Update `prune_backups` there and run `pbs apply` to reconcile. |
| 178 | + |
| 179 | +**Note**: As of 2026-02-28, the YAML config (`keep-daily=7, weekly=4, monthly=3`) is out of sync with the live cluster job (`keep-daily=3, weekly=2`). Reconcile when appropriate. |
| 180 | + |
| 181 | +## Troubleshooting |
| 182 | + |
| 183 | +| Symptom | Cause | Fix | |
| 184 | +|---------|-------|-----| |
| 185 | +| GC fails with ENOSPC | Pool 100% full | Free ZFS-level space first (see Emergency section) | |
| 186 | +| VM stopped after backup | Freeze/thaw timeout on full disk | Free space, start VM | |
| 187 | +| Backup fails with "storage inactive" | PBS LXC 103 down or DNS issue | `pct start 103`, check DNS | |
| 188 | +| Prune removes nothing | All snapshots within retention window | Expected behavior — check with `pvesm list` | |
| 189 | +| GC removes 0 bytes | No dead chunks (nothing pruned recently) | Run prune first, then GC | |
| 190 | +| High dedup ratio (>15x) | Many similar VMs being backed up | Normal for homelab | |
| 191 | +| Verify shows errors | Bit rot or corrupt chunks | Restore affected backups to verify, re-backup if needed | |
| 192 | + |
| 193 | +## Monitoring (TODO) |
| 194 | + |
| 195 | +**Not yet implemented** — see RCA `pbs-disk-full-vm-crash-rca.md` for context. |
| 196 | + |
| 197 | +Needed: |
| 198 | +- ZFS pool space alerting (warn 80%, critical 90%) |
| 199 | +- Backup failure notifications to HA |
| 200 | +- VM status monitoring (detect crashed VMs) |
| 201 | + |
| 202 | +## Related Documents |
| 203 | + |
| 204 | +- RCA: `docs/runbooks/pbs-disk-full-vm-crash-rca.md` |
| 205 | +- PBS migration: `docs/runbooks/pbs-migration-to-pumped-piglet.md` |
| 206 | +- PBS connectivity: `docs/runbooks/proxmox-backup-server-storage-connectivity.md` |
| 207 | +- Secret management: `docs/secret-management.md` |
| 208 | +- PBS CLI tool: `proxmox/homelab/src/homelab/pbs_cli.py` |
| 209 | +- PBS config: `proxmox/homelab/config/pbs-storage.yaml` |
| 210 | + |
| 211 | +## Tags |
| 212 | + |
| 213 | +pbs, proxmox-backup-server, backup, vzdump, prune, garbage-collection, gc, verify, retention, zfs, disk-full, enospc, maintenance, schedule, datastore, homelab-backup, pumped-piglet, lxc-103, runbook, disaster-recovery, proxmox, pve, proxmocks |
| 214 | + |
| 215 | +**Owner**: Homelab |
| 216 | +**Last Updated**: 2026-02-28 |
0 commit comments