F4.f — Caddy full routing cutover¶
Migra LXC 270/271 de passthrough (proxy a VM 208 Caddy) a full routing (LXC Caddy importa managed.caddy + direct reverse_proxy a containers VM 208). Apaga Caddy VM 208 al final.
✅ Completado 2026-05-20¶
Cutover ejecutado con éxito. Estado actual:
- VM 208 Caddy container:
STOPPED(compose servicecaddyparado, no removido — recuperable condocker compose start caddypara rollback de emergencia). - LXC 270 / 271: full routing activo.
Caddyfileapunta amanaged.caddycon host:port targets (192.168.0.208:<host_port>). homelab-ctl.py: refactorizado para emitir host:port targets y auto-rsyncmanaged.caddya LXC 270 y LXC 271 trassync.- VIP
.250: keepalived MASTER en 270 (.247), BACKUP en 271 (.248). Failover testeado<2s.
Tras la primera tentativa fallida del cutover (Docker DNS targets <container_name>:<port> no resoluble desde LXC porque no estaba en stacks_shared network), se refactorizó homelab-ctl.py para emitir host:port. Re-attempt OK.
Pre-cutover checklist¶
- Backup completo VM 208 (PBS) verificado de hoy
- Snapshot ZFS LXC 270 + 271 manual
- Sablier expuesto en host port 10000 (verifica:
curl http://192.168.0.208:10000/health→ "OK") -
homelab-ctl.pyconSABLIER_URL=http://192.168.0.208:10000(verifica:grep SABLIER_URL ~/scripts/homelab-ctl.py) - managed.caddy en ambos LXC con timestamp <5 min ago (verifica:
ls -la /etc/caddy/managed.caddyen root@LXC) - Caddyfile.full-routing-staged validated en ambos LXC (
caddy validate --config /etc/caddy/Caddyfile.full-routing-staged) - Ventana de mantenimiento anunciada (~5min downtime potential)
Cutover steps (10-15 min)¶
1. Stop Caddy VM 208 (libera ports 80/443 en host)¶
A partir de aquí, NADIE responde HTTPS hasta paso 2 — ventana de ~30s downtime.
2. Apply full routing en LXC primary (270)¶
cd ~/docs-repo/ansible
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
ansible-playbook playbook.yml --tags caddy_full_routing_apply --limit caddy-primary
Esto:
- Backup /etc/caddy/Caddyfile → Caddyfile.bak.passthrough
- Swap Caddyfile.full-routing-staged → Caddyfile
- Install new caddy.service con EnvironmentFile=/etc/caddy/.env
- systemctl daemon-reload + restart caddy
- Smoke test bookmarks.monxas.casa (debe ser 200/30x)
3. Validate external¶
for h in bookmarks grafana pocketid pics home; do
CODE=$(curl -s --max-time 10 -o /dev/null -w "%{http_code}" https://$h.monxas.casa/)
printf "%-30s %s\n" "$h.monxas.casa" "$CODE"
done
Si 5/5 OK, continuar paso 4. Si fail, ir a Rollback.
4. Apply LXC secondary (271)¶
5. Test failover¶
ssh [email protected] systemctl stop caddy
sleep 8
curl -s https://bookmarks.monxas.casa/ -o /dev/null -w "%{http_code}\n" # debe 30x (via 271)
ssh [email protected] systemctl start caddy # VIP regresa a 270
6. Decide on VM 208 Caddy¶
Opciones:
- Mantener stopped indefinidamente: containers backends siguen corriendo. Caddy compose está parado pero recuperable. Recomendado primera semana.
- docker compose rm caddy: borra el container. Si quieres revertir → docker compose up -d caddy.
- tunnel-sync remove: ya no aplica (homelab-ctl.py sync sigue funcionando per generar managed.caddy en VM 208 + Ansible despliega a LXCs).
Rollback (si paso 3 falla)¶
# Restore Caddyfile passthrough en LXC 270
ssh [email protected] 'cp /etc/caddy/Caddyfile.bak.passthrough /etc/caddy/Caddyfile && systemctl restart caddy'
# Start Caddy VM 208 back
ssh media-208 'cd /home/monxas/stacks/reverse-proxy && docker compose start caddy'
# Verify
curl -s -o /dev/null -w "%{http_code}\n" https://bookmarks.monxas.casa/ # expect 307
Tiempo total rollback: ~30s + ~10s para Caddy VM 208 startup.
Post-cutover ongoing maintenance¶
Workflow de añadir nuevo hostname post-F4.f:
- Editar compose labels (
tunnel.hostname=foo.monxas.casa,tunnel.port=<host_port>) →docker compose up -d. python3 ~/scripts/homelab-ctl.py sync --yes— regeneramanaged.caddyen VM 208 y auto-rsync a LXC 270 + LXC 271 + recarga Caddy en ambos.- Listo. Fallback Ansible:
ansible-playbook ~/docs-repo/ansible/playbook.yml --tags caddy_full_routing_stage --limit caddy_lxc.
Containers sin host_port
Cualquier nuevo container debe exponer puerto al host (ports: ['<host>:<container>']). Si solo tiene expose: interno, no será alcanzable desde LXC Caddy.
Gotchas conocidos¶
- Sablier puerto 10000: si VM 208 cae,
homelab-ctl.py synco LXC Caddy plugin Sablier no podrá contactarlo. Las apps lazy quedan en wait page indefinida. Aceptar como tradeoff (Sablier es opcional, podría parsing a otro lugar). - Cert wildcard via DNS-01: primera vez LXC Caddy arranca, ACME pide cert. Tarda ~30-60s. Plan downtime adicional.
- Smoke test del play assume bookmarks 200/30x: si Karakeep está cayendo, paso 2 da falso positivo. Reemplaza por
pocketid.monxas.casa(más estable) enansible/playbook.ymltask "Wait 10s + smoke test" si quieres más confianza. - managed.caddy timestamp drift: si el cutover tarda >15min y haces deploy de nuevo container entre paso 1 y 2, managed.caddy en VM 208 cambia pero LXC no. Apply en paso 2 va con managed.caddy viejo. Re-stage primero si dudas.