Post-Snapshot / AMI Setup
When you create a new server from a Hetzner snapshot or AWS AMI, the cloned disk still carries the original server's IP addresses, Frappe API token, and port configuration. The post-snapshot command automates all the fixes in a single step.
Any time you copy a running CTMS deployment to a new server with a different IP address — Hetzner snapshots, AWS AMIs, or any disk-image clone.
Usage
ssh root@<new-server-ip>
cd /opt/ctms-deployment
SERVER_HOST=<new-server-ip> ./zynctl.sh post-snapshot
The command auto-detects the old IP from .env.production and runs 6 steps:
| Step | Action | Details |
|---|---|---|
| 1 | Secure MariaDB | Removes exposed port 3306 from Frappe compose; also fixes a known 8080:80 port/volume bug if present |
| 2 | Update IPs | Replaces old IP → new IP in .env.production and .env |
| 3 | Restart Frappe | docker compose down + up -d; waits up to 60 s for configurator to exit and backend to be running; verifies ≥100 DB tables |
| 4 | Refresh token | Calls refresh-token sub-command — tests current token, regenerates if invalid (401), patches .env.production, recreates API gateway + Zynexa |
| 5 | Recreate services | $DC up -d --force-recreate for all CTMS services; waits 10 s |
| 6 | Verify | Checks Frappe direct auth, KrakenD gateway, token consistency across gateway + Zynexa, patient count, container count |
After completion the script prints access URLs:
Zynexa: http://<new-ip>:3000
Frappe: http://<new-ip>:8080
Gateway: http://<new-ip>:9080
Zynexa redirects unauthenticated requests to the login page, so the health check returns 307 — not 200. This is expected behavior. The verification step in zynctl.sh currently treats only 200 as a pass, so you may see ❌ for Zynexa even when it is healthy.
After post-snapshot completes, run the full Deployment Verification recipe to confirm all 10 services and seed data are intact.
Creating a Snapshot
Hetzner Cloud
- Cloud Console → Servers → select server → Snapshots → Create Snapshot
- Create new server from snapshot: Add Server → Snapshots tab → select it
- SSH in → run
post-snapshotwith the new IP
AWS (Future)
- Create an AMI from a running EC2 instance → launch a new instance from it
- SSH in → run
SERVER_HOST=<public-ip> ./zynctl.sh post-snapshot
AMI-specific automation (e.g., auto-detecting IP from instance metadata) is not yet implemented. Pass the IP manually via SERVER_HOST.
Snapshots are ideal for demo/staging clones — spin up a pre-configured instance in minutes instead of the full 15-minute deploy.
Troubleshooting
| Problem | Solution |
|---|---|
Could not detect old IP | Ensure EC2_PUBLIC_IP is set in .env.production |
| Frappe configurator non-zero exit | docker logs frappe-marley-health-configurator-1 — usually DB connectivity |
| Gateway 401 after completion | $DC up -d --force-recreate api-gateway — token didn't propagate. See Frappe Token Refresh. |
| Browser shows old IP | Hard-refresh (Ctrl+Shift+R) — cached NEXTAUTH_URL |
| < 100 DB tables | Snapshot taken before provisioning finished — run ./zynctl.sh resume-deploy |
| Zynexa shows ❌ but works in browser | Zynexa returns HTTP 307 (redirect). This is normal — see note above. |
| Missing Item Groups (Laboratory, Drug) | Snapshot was taken from a pre-v2.30 deployment. Run ./zynctl.sh resume-deploy or manually trigger ctms-init Stage 4. See Seed & Init. |
| ctms-init uses stale image | Run docker compose pull ctms-init before docker compose up ctms-init. Fixed automatically in bundle ≥v2.31. |
Related
- Spin Up a VM in Hetzner
- Bundle Deployment — full deploy from scratch
- Deployment Verification — end-to-end checks to run after post-snapshot
- Fix Frappe API Token After Restart — what Step 4 does under the hood
- Seed & Init Commands — re-run individual provisioning stages
- Platform Runbook