claude-home/server-configs/caddy-migration/MIGRATION.md
Cal Corum c08e779e42 docs: add caddy migration config, tdarr flow backup, and troubleshooting updates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:13:21 -05:00

6.5 KiB

NPM to Caddy Migration Plan

Step-by-step guide to migrate from Nginx Proxy Manager to Caddy on 10.10.0.16.

Prerequisites

  • Cloudflare API token with Zone:DNS:Edit for manticorum.com
  • SSH access to pihole (10.10.0.16) and ubuntu-manticore (10.10.0.226)
  • Docker and docker compose installed on 10.10.0.16
  • Familiarity with current NPM proxy hosts (see README.md)

Phase 1: Prepare (no downtime, no changes to production)

1.1 Create Cloudflare API Token

  1. Go to https://dash.cloudflare.com/profile/api-tokens
  2. Create token with permissions:
    • Zone - DNS - Edit (scoped to manticorum.com)
  3. Save the token securely

1.2 Deploy Caddy Config to Host

# From workstation
scp -r server-configs/caddy-migration/ pihole:/home/cal/caddy/
ssh pihole "cp /home/cal/caddy/.env.example /home/cal/caddy/.env"

Edit .env on the host:

ssh pihole "nano /home/cal/caddy/.env"
# Set CF_API_TOKEN=<your token>

1.3 Build and Test Caddy (on alternate ports)

Temporarily modify docker-compose.yml to use non-conflicting ports:

ports:
  - "8080:80"
  - "8443:443"
  - "8443:443/udp"
ssh pihole "cd /home/cal/caddy && docker compose up -d --build"

Verify the container starts and the Cloudflare module is loaded:

ssh pihole "docker logs caddy 2>&1 | head -30"
ssh pihole "docker exec caddy caddy list-modules | grep cloudflare"

Verify config is valid:

ssh pihole "docker exec caddy caddy validate --config /etc/caddy/Caddyfile"

Test a proxy host directly (bypass DNS):

curl -k --resolve sbadev.manticorum.com:8443:10.10.0.16 https://sbadev.manticorum.com:8443/

1.4 Verify Cert Issuance

Check that Caddy successfully obtains a wildcard cert:

ssh pihole "docker logs caddy 2>&1 | grep -i 'certificate\|tls\|acme'"

You should see successful ACME DNS-01 challenge completion.

1.5 Stop Test Caddy

ssh pihole "cd /home/cal/caddy && docker compose down"

Revert docker-compose.yml ports back to 80/443.

Phase 2: Cutover (brief downtime)

Expected downtime: < 2 minutes (stop NPM, start Caddy, sync DNS).

2.1 Backup NPM

ssh pihole "cd ~/nginx-proxy-manager && tar czf ~/npm-backup-$(date +%Y%m%d).tar.gz data/ letsencrypt/"

2.2 Take a Snapshot

If 10.10.0.16 is a VM/LXC on Proxmox, take a snapshot first:

# From proxmox host (adjust VMID)
pct snapshot <VMID> pre-caddy-migration

2.3 Stop NPM

ssh pihole "cd ~/nginx-proxy-manager && docker compose down"

Ports 80, 443, and 81 are now free.

2.4 Start Caddy

ssh pihole "cd /home/cal/caddy && docker compose up -d"

2.5 Verify Services

Quick smoke test of key services:

# Test from workstation (DNS should already point to 10.10.0.16 via Pi-hole)
curl -sI https://git.manticorum.com | head -5
curl -sI https://n8n.manticorum.com | head -5
curl -sI https://jellyfin.manticorum.com | head -5
curl -sI https://foundry.manticorum.com | head -5
curl -sI https://status.manticorum.com | head -5

# Test internal-only access
curl -sI https://radarr.manticorum.com | head -5   # should work from local
curl -sI https://sonarr.manticorum.com | head -5

2.6 Update Pi-hole Sync

Deploy the new sync script:

ssh pihole "cp /home/cal/caddy/scripts/caddy-pihole-sync.sh /home/cal/scripts/"
ssh pihole "chmod +x /home/cal/scripts/caddy-pihole-sync.sh"

Test dry run:

ssh pihole "/home/cal/scripts/caddy-pihole-sync.sh --dry-run"

Run sync:

ssh pihole "/home/cal/scripts/caddy-pihole-sync.sh"

Update cron to use the new script:

ssh pihole "crontab -l | sed 's|npm-pihole-sync.sh|caddy-pihole-sync.sh|g' | crontab -"

Also update the CADDYFILE path variable in the script if the deployment path differs from /home/cal/caddy/Caddyfile.

Phase 3: Validate (next 24-48 hours)

3.1 Monitor Caddy Logs

ssh pihole "docker logs caddy -f"

Look for:

  • Successful TLS handshakes
  • No upstream connection errors
  • Cert renewal events (if timing aligns)

3.2 Check Uptime Kuma

Verify all monitored services at https://status.manticorum.com show UP.

3.3 Test WebSocket Services

These services use WebSockets and should be tested interactively:

  • Foundry VTT (foundry.manticorum.com) - open a game session
  • n8n (n8n.manticorum.com) - open workflow editor
  • Memos (memos.manticorum.com) - create/edit a memo
  • Termix (termix.manticorum.com) - open a terminal session

3.4 Test External Access

If any services are accessed via Cloudflare from outside:

  1. From a phone on cellular (not on home WiFi)
  2. Access public services and verify they load
  3. Access internal-only services and verify 403 response

3.5 Verify Access Restrictions

# From a machine NOT on 10.0.0.0/23 or 10.10.0.0/24:
curl -sI https://radarr.manticorum.com  # Should return 403
curl -sI https://sonarr.manticorum.com  # Should return 403

Phase 4: Cleanup

4.1 Remove NPM (after validation period)

# Keep backup, remove containers and images
ssh pihole "cd ~/nginx-proxy-manager && docker compose rm -f"
ssh pihole "docker image rm jc21/nginx-proxy-manager:latest"

4.2 Update Documentation

  • Update server-configs/networking/nginx-proxy-manager-pihole.md to reference Caddy
  • Update any Uptime Kuma monitors that check port 81 (NPM admin)
  • Update CONTEXT.md networking section

4.3 Free Port 81

Port 81 (NPM admin UI) is no longer needed. Caddy's admin API runs on localhost:2019 inside the container by default (not exposed).

Rollback Plan

If something goes wrong, rollback takes < 1 minute:

# Stop Caddy
ssh pihole "cd /home/cal/caddy && docker compose down"

# Restart NPM
ssh pihole "cd ~/nginx-proxy-manager && docker compose up -d"

# Revert cron to old sync script
ssh pihole "crontab -l | sed 's|caddy-pihole-sync.sh|npm-pihole-sync.sh|g' | crontab -"

Or restore from Proxmox snapshot:

pct rollback <VMID> pre-caddy-migration

Adding New Services After Migration

Edit the Caddyfile and reload - no web UI needed:

ssh pihole "nano /home/cal/caddy/Caddyfile"

# Add a new block:
# newservice.manticorum.com {
#     reverse_proxy 10.10.0.xxx:port {
#         import proxy_headers
#     }
# }

# Validate
ssh pihole "docker exec caddy caddy validate --config /etc/caddy/Caddyfile"

# Apply (zero downtime)
ssh pihole "docker exec caddy caddy reload --config /etc/caddy/Caddyfile"

# Sync DNS to Pi-holes
ssh pihole "/home/cal/scripts/caddy-pihole-sync.sh"