|
| 1 | +# Using Oxide Crucible as a Secrets Backup for Your Homelab |
| 2 | + |
| 3 | +**Date**: January 26, 2026 |
| 4 | +**Author**: Claude + Human collaboration |
| 5 | +**Tags**: oxide, crucible, secrets, backup, homelab, sops, ssh, configuration |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## The Problem: Where Do You Put the Stuff That Can't Go in Git? |
| 10 | + |
| 11 | +Every homelab has secrets that can't be checked into version control: |
| 12 | + |
| 13 | +- SOPS age keys (the key that decrypts your other keys) |
| 14 | +- SSH private keys |
| 15 | +- `.env` files with API tokens and passwords |
| 16 | +- Home Assistant `secrets.yaml` |
| 17 | +- Database credentials |
| 18 | +- Certificate private keys |
| 19 | + |
| 20 | +These files are critical. Lose them and you're locked out of your own infrastructure. But where do you store them safely? |
| 21 | + |
| 22 | +Cloud storage? Now your secrets depend on someone else's infrastructure. Local backup drive? Single point of failure. Password manager? Doesn't handle files well. |
| 23 | + |
| 24 | +## The Solution: Distributed Storage You Control |
| 25 | + |
| 26 | +I already had [Oxide Crucible running on a $30 mini PC](/docs/blog/2026-01-25-budget-oxide-storage-sled.md) for VM storage experiments. Then it hit me: Crucible provides 12.5GB of distributed block storage to each of my 5 Proxmox hosts. That's plenty of space for configs and secrets. |
| 27 | + |
| 28 | +``` |
| 29 | +┌─────────────────────────────────────────────────────────────────┐ |
| 30 | +│ Secrets Backup Architecture │ |
| 31 | +├─────────────────────────────────────────────────────────────────┤ |
| 32 | +│ │ |
| 33 | +│ Mac (source of truth) │ |
| 34 | +│ ~/.config/sops/age/keys.txt ─────┐ │ |
| 35 | +│ ~/.ssh/id_ed25519_pve ───────────┼──► sync-secrets-to-crucible│ |
| 36 | +│ ~/code/home/.env ────────────────┘ │ │ |
| 37 | +│ │ │ |
| 38 | +│ ▼ │ |
| 39 | +│ ┌─────────────────────────────────────────────────────────┐ │ |
| 40 | +│ │ pve:/mnt/crucible-storage/ │ │ |
| 41 | +│ │ secrets/ │ │ |
| 42 | +│ │ ├── sops/age.key │ │ |
| 43 | +│ │ ├── ssh/id_ed25519_pve │ │ |
| 44 | +│ │ └── env/homelab.env │ │ |
| 45 | +│ │ services/ │ │ |
| 46 | +│ │ └── haos/20260126-231325/ │ │ |
| 47 | +│ │ ├── configuration.yaml │ │ |
| 48 | +│ │ ├── automations.yaml │ │ |
| 49 | +│ │ └── secrets.yaml │ │ |
| 50 | +│ └─────────────────────────────────────────────────────────┘ │ |
| 51 | +│ │ │ |
| 52 | +│ │ replicate-crucible-storage │ |
| 53 | +│ ▼ │ |
| 54 | +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ |
| 55 | +│ │still-fawn│ │pumped- │ │chief- │ │fun- │ │ |
| 56 | +│ │ /mnt/ │ │piglet │ │horse │ │bedbug │ │ |
| 57 | +│ │crucible- │ │ /mnt/ │ │ /mnt/ │ │ /mnt/ │ │ |
| 58 | +│ │storage/ │ │crucible- │ │crucible- │ │crucible- │ │ |
| 59 | +│ │ │ │storage/ │ │storage/ │ │storage/ │ │ |
| 60 | +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ |
| 61 | +│ │ │ │ │ │ |
| 62 | +│ └────────────┴─────┬──────┴────────────┘ │ |
| 63 | +│ │ │ |
| 64 | +│ ▼ │ |
| 65 | +│ ┌─────────────────────────┐ │ |
| 66 | +│ │ proper-raptor │ │ |
| 67 | +│ │ (Crucible storage) │ │ |
| 68 | +│ │ 192.168.4.189 │ │ |
| 69 | +│ └─────────────────────────┘ │ |
| 70 | +│ │ |
| 71 | +└─────────────────────────────────────────────────────────────────┘ |
| 72 | +``` |
| 73 | + |
| 74 | +## What Gets Backed Up |
| 75 | + |
| 76 | +### Local Secrets (from Mac) |
| 77 | + |
| 78 | +```bash |
| 79 | +./scripts/crucible/sync-secrets-to-crucible.sh |
| 80 | +``` |
| 81 | + |
| 82 | +| File | Purpose | |
| 83 | +|------|---------| |
| 84 | +| `~/.config/sops/age/keys.txt` | SOPS decryption key | |
| 85 | +| `~/.ssh/id_ed25519_pve` | Proxmox SSH key | |
| 86 | +| `~/code/home/.env` | Homelab credentials | |
| 87 | + |
| 88 | +### Service Configs (from VMs/containers) |
| 89 | + |
| 90 | +```bash |
| 91 | +./scripts/crucible/backup-services-to-crucible.sh |
| 92 | +``` |
| 93 | + |
| 94 | +| Service | Files Backed Up | |
| 95 | +|---------|-----------------| |
| 96 | +| Home Assistant | `configuration.yaml`, `automations.yaml`, `secrets.yaml`, `core.config_entries` | |
| 97 | +| Frigate | `config.yml`, model list | |
| 98 | +| Ollama | Model list, model details | |
| 99 | + |
| 100 | +### Proxmox Host Configs |
| 101 | + |
| 102 | +```bash |
| 103 | +./scripts/crucible/backup-configs-to-crucible.sh |
| 104 | +``` |
| 105 | + |
| 106 | +| Config | Purpose | |
| 107 | +|--------|---------| |
| 108 | +| `/etc/pve/*` | Cluster config, VM/LXC definitions, storage.cfg | |
| 109 | +| `/etc/network/interfaces` | Network configuration | |
| 110 | +| `/etc/systemd/system/crucible-*` | Crucible service definitions | |
| 111 | +| ZFS pool/dataset lists | Storage topology | |
| 112 | + |
| 113 | +## Replication: 5 Copies of Everything |
| 114 | + |
| 115 | +The magic happens with replication. After backing up to pve, one command copies everything to all other hosts: |
| 116 | + |
| 117 | +```bash |
| 118 | +./scripts/crucible/replicate-crucible-storage.sh |
| 119 | +``` |
| 120 | + |
| 121 | +``` |
| 122 | +[23:16:40] Starting Crucible storage replication |
| 123 | +[23:16:40] Primary: pve |
| 124 | +[23:16:40] Replicas: still-fawn.maas pumped-piglet.maas chief-horse.maas fun-bedbug.maas |
| 125 | +
|
| 126 | +=== still-fawn.maas === |
| 127 | +[23:16:41] Syncing secrets to still-fawn.maas... |
| 128 | +[23:16:42] Syncing services to still-fawn.maas... |
| 129 | +
|
| 130 | +=== pumped-piglet.maas === |
| 131 | +[23:16:43] Syncing secrets to pumped-piglet.maas... |
| 132 | +... |
| 133 | +
|
| 134 | +[23:16:48] Storage contents: |
| 135 | +--- pve --- |
| 136 | +44K /mnt/crucible-storage/secrets/ |
| 137 | +104K /mnt/crucible-storage/services/ |
| 138 | +--- still-fawn.maas --- |
| 139 | +44K /mnt/crucible-storage/secrets/ |
| 140 | +104K /mnt/crucible-storage/services/ |
| 141 | +... |
| 142 | +``` |
| 143 | + |
| 144 | +Now the same secrets exist on all 5 Proxmox hosts. If one host dies, four others have copies. |
| 145 | + |
| 146 | +## The Scripts |
| 147 | + |
| 148 | +All scripts are idempotent - safe to run repeatedly. |
| 149 | + |
| 150 | +### sync-secrets-to-crucible.sh |
| 151 | + |
| 152 | +```bash |
| 153 | +#!/bin/bash |
| 154 | +# Syncs local secrets from Mac to Crucible storage |
| 155 | + |
| 156 | +LOCAL_SECRETS=( |
| 157 | + "sops/age.key:$HOME/.config/sops/age/keys.txt" |
| 158 | + "ssh/id_ed25519_pve:$HOME/.ssh/id_ed25519_pve" |
| 159 | + "env/homelab.env:$REPO_ROOT/proxmox/homelab/.env" |
| 160 | +) |
| 161 | + |
| 162 | +for pair in "${LOCAL_SECRETS[@]}"; do |
| 163 | + dest_path="${pair%%:*}" |
| 164 | + src_path="${pair#*:}" |
| 165 | + scp "$src_path" "root@pve:/mnt/crucible-storage/secrets/$dest_path" |
| 166 | +done |
| 167 | +``` |
| 168 | + |
| 169 | +### backup-services-to-crucible.sh |
| 170 | + |
| 171 | +Uses `qm guest exec` to pull configs from HAOS VM: |
| 172 | + |
| 173 | +```bash |
| 174 | +# HAOS config lives at /mnt/data/supervisor/homeassistant/ |
| 175 | +ssh "root@chief-horse.maas" " |
| 176 | + qm guest exec 116 -- cat '$ha_config/configuration.yaml' | \ |
| 177 | + jq -r '.[\"out-data\"]' > '$dest_dir/configuration.yaml' |
| 178 | + qm guest exec 116 -- cat '$ha_config/secrets.yaml' | \ |
| 179 | + jq -r '.[\"out-data\"]' > '$dest_dir/secrets.yaml' |
| 180 | +" |
| 181 | +``` |
| 182 | + |
| 183 | +### replicate-crucible-storage.sh |
| 184 | + |
| 185 | +Uses tar pipe through local machine (avoids SSH host key issues between Proxmox hosts): |
| 186 | + |
| 187 | +```bash |
| 188 | +for replica in "${REPLICA_HOSTS[@]}"; do |
| 189 | + ssh "root@$PRIMARY_HOST" "tar -C '$src' -cf - ." | \ |
| 190 | + ssh "root@$replica" "tar -C '$dest' -xf -" |
| 191 | +done |
| 192 | +``` |
| 193 | + |
| 194 | +## Why This Works |
| 195 | + |
| 196 | +1. **No cloud dependency** - Everything stays on your network |
| 197 | +2. **Distributed** - 5 copies across 5 hosts |
| 198 | +3. **Fast recovery** - Secrets are already on every Proxmox host |
| 199 | +4. **Git-friendly** - Scripts are version controlled, secrets aren't |
| 200 | +5. **Cheap** - Uses existing Crucible storage (costs $0 extra) |
| 201 | + |
| 202 | +## Recovery Scenarios |
| 203 | + |
| 204 | +### Lost my Mac |
| 205 | + |
| 206 | +SSH to any Proxmox host, secrets are at `/mnt/crucible-storage/secrets/`: |
| 207 | + |
| 208 | +```bash |
| 209 | +ssh root@pve |
| 210 | +cat /mnt/crucible-storage/secrets/sops/age.key |
| 211 | +cat /mnt/crucible-storage/secrets/ssh/id_ed25519_pve |
| 212 | +``` |
| 213 | + |
| 214 | +### Proxmox host died |
| 215 | + |
| 216 | +Four other hosts have identical copies. Pick one. |
| 217 | + |
| 218 | +### proper-raptor (Crucible storage) died |
| 219 | + |
| 220 | +This is the single point of failure today. The `/mnt/crucible-storage` would become unavailable. But! The last-synced data still exists on each host's local mount until you disconnect NBD. |
| 221 | + |
| 222 | +**Future improvement**: Add 2 more MA90 sleds (~$60) for true 3-way Crucible replication. Then even the storage backend survives hardware failure. |
| 223 | + |
| 224 | +### Home Assistant config corrupted |
| 225 | + |
| 226 | +Restore from timestamped backup: |
| 227 | + |
| 228 | +```bash |
| 229 | +ls /mnt/crucible-storage/services/haos/ |
| 230 | +# 20260125-231325/ 20260126-080000/ ... |
| 231 | + |
| 232 | +cat /mnt/crucible-storage/services/haos/20260125-231325/configuration.yaml |
| 233 | +``` |
| 234 | + |
| 235 | +## Automation Ideas |
| 236 | + |
| 237 | +Add to cron for automatic backups: |
| 238 | + |
| 239 | +```bash |
| 240 | +# Daily secrets sync (2 AM) |
| 241 | +0 2 * * * /home/user/code/home/scripts/crucible/sync-secrets-to-crucible.sh |
| 242 | + |
| 243 | +# Daily service backup (3 AM) |
| 244 | +0 3 * * * /home/user/code/home/scripts/crucible/backup-services-to-crucible.sh |
| 245 | + |
| 246 | +# Daily replication (4 AM) |
| 247 | +0 4 * * * /home/user/code/home/scripts/crucible/replicate-crucible-storage.sh |
| 248 | +``` |
| 249 | + |
| 250 | +Or trigger on git push with a hook. |
| 251 | + |
| 252 | +## Total Cost |
| 253 | + |
| 254 | +| Item | Cost | |
| 255 | +|------|------| |
| 256 | +| MA90 mini PC (Crucible storage) | $30 | |
| 257 | +| USB 2.5GbE adapters (5x) | $75 | |
| 258 | +| 2.5GbE switch | $60 | |
| 259 | +| **Total** | **$165** | |
| 260 | + |
| 261 | +For that, you get distributed secrets storage across 5 hosts with 12.5GB per host. Not bad for a homelab. |
| 262 | + |
| 263 | +## Conclusion |
| 264 | + |
| 265 | +Crucible wasn't designed for secrets backup. It's enterprise distributed block storage for VMs. But the primitives it provides - replicated storage accessible from multiple hosts - solve the secrets problem elegantly. |
| 266 | + |
| 267 | +Sometimes the best tool for a job is the one you already have running. |
| 268 | + |
| 269 | +--- |
| 270 | + |
| 271 | +**Related posts:** |
| 272 | +- [Budget Oxide Storage Sled](/docs/blog/2026-01-25-budget-oxide-storage-sled.md) |
| 273 | + |
| 274 | +**Scripts:** |
| 275 | +- [scripts/crucible/sync-secrets-to-crucible.sh](/scripts/crucible/sync-secrets-to-crucible.sh) |
| 276 | +- [scripts/crucible/backup-services-to-crucible.sh](/scripts/crucible/backup-services-to-crucible.sh) |
| 277 | +- [scripts/crucible/backup-configs-to-crucible.sh](/scripts/crucible/backup-configs-to-crucible.sh) |
| 278 | +- [scripts/crucible/replicate-crucible-storage.sh](/scripts/crucible/replicate-crucible-storage.sh) |
0 commit comments