Skip to main content

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.

When Do You Need This?

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:

StepActionDetails
1Secure MariaDBRemoves exposed port 3306 from Frappe compose; also fixes a known 8080:80 port/volume bug if present
2Update IPsReplaces old IP → new IP in .env.production and .env
3Restart Frappedocker compose down + up -d; waits up to 60 s for configurator to exit and backend to be running; verifies ≥100 DB tables
4Refresh tokenCalls refresh-token sub-command — tests current token, regenerates if invalid (401), patches .env.production, recreates API gateway + Zynexa
5Recreate services$DC up -d --force-recreate for all CTMS services; waits 10 s
6VerifyChecks 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 returns HTTP 307

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.

Full verification

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

  1. Cloud Console → Servers → select server → SnapshotsCreate Snapshot
  2. Create new server from snapshot: Add ServerSnapshots tab → select it
  3. SSH in → run post-snapshot with the new IP

AWS (Future)

  1. Create an AMI from a running EC2 instance → launch a new instance from it
  2. SSH in → run SERVER_HOST=<public-ip> ./zynctl.sh post-snapshot
note

AMI-specific automation (e.g., auto-detecting IP from instance metadata) is not yet implemented. Pass the IP manually via SERVER_HOST.

tip

Snapshots are ideal for demo/staging clones — spin up a pre-configured instance in minutes instead of the full 15-minute deploy.


Troubleshooting

ProblemSolution
Could not detect old IPEnsure EC2_PUBLIC_IP is set in .env.production
Frappe configurator non-zero exitdocker 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 IPHard-refresh (Ctrl+Shift+R) — cached NEXTAUTH_URL
< 100 DB tablesSnapshot taken before provisioning finished — run ./zynctl.sh resume-deploy
Zynexa shows ❌ but works in browserZynexa 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 imageRun docker compose pull ctms-init before docker compose up ctms-init. Fixed automatically in bundle ≥v2.31.