Deployment guide
Production runs on a single AWS Lightsail host with Docker Compose, fronted by Caddy (TLS) and Cloudflare (DNS/CDN). There is no auto‑deploy pipeline — deploys are a manual, per‑service rebuild on the server. This page is the runbook your team follows.
Topology
flowchart LR
U["Users"] --> CF["Cloudflare"] --> CADDY["Caddy (host)"]
CADDY --> WEB["axon_web :4300"]
CADDY --> ADMIN["axon_admin :4200"]
CADDY --> GW["axon_api_gateway :3000"]
GW --> SVC["12 backend services (private network)"]
SVC --> PG[("axon_postgres")]
SVC --> RED[("axon_redis")]
web.vistus.io→ web app,api.vistus.io→ gateway (Caddy routes by host).- Backend services are reachable only on the internal
axon_netDocker network.
Compose files — always pass both
The stack is split across two files and they must be used together:
docker-compose.yml— base: theaxon_netnetwork,postgres,redis,db-migrate, and all 12 backend services.docker-compose.prod.yml— prod overlay:web,admin,caddy, and the frontend build args (sourced from the server's.env).
# Define this once per shell session on the server:
DC="docker compose -f docker-compose.yml -f docker-compose.prod.yml"
Using only the prod file fails with
service "web" refers to undefined network axon_net— becauseaxon_netandapi-gatewaylive in the base file.
Standard deploy (one service)
Most deploys change one or a few services. The flow is: push → pull → rebuild that image → recreate just that container.
# 1. Local: commit & push
git add <changed files> && git commit -m "..." && git push origin main
# 2. Server: pull and rebuild ONLY the changed service(s)
cd /path/to/axon-mvp
git pull origin main
$DC build --no-cache <service> # e.g. web, service-funding, api-gateway
$DC up -d --no-deps <service> # --no-deps = don't restart its dependencies
$DC logs -f <service> # watch it boot, Ctrl+C when steady
--no-cacheguarantees the new source is picked up (worth it; frontend builds peak ~4 GB RAM and take a few minutes).--no-depskeeps the rest of the stack running — only the named container is recreated.- The web bundle is cached in the browser, so after a
webdeploy, hard‑refresh (Cmd/Ctrl+Shift+R) to load the new JS.
What to rebuild when
| You changed… | Rebuild |
|---|---|
| A backend service's code | that service-* |
Shared package (packages/*) |
every service that imports it (often simplest: all backend) |
| Prisma schema | run a migration first (see Migrations), then the services |
| Web/admin/mobile UI | web and/or admin |
NEXT_PUBLIC_* env value |
rebuild web (baked at build time) |
| The escrow contract | redeploy the contract, set the new address in .env, rebuild agreements + funding + settlements + reconciliation |
Rollback
# revert the commit and redeploy
git revert <sha> && git push origin main
# on server: git pull && $DC build --no-cache <service> && $DC up -d --no-deps <service>
The currently‑running container keeps serving until the new up -d succeeds, so a failed build won't take the site down.
First-time / full bring-up
$DC build # build all images
$DC up -d # start everything (db-migrate runs once and exits)
$DC ps # verify all Up (db-migrate Exited is expected)
See the repo's docs/CLOUD_DEPLOY_LIGHTSAIL.md and docs/CLOUDFLARE_TUNNEL_SETUP.md for host provisioning and DNS.