Production overview
Production runs on Docker Compose, on one Linux server. Aspire is for development
only: the services read ordinary configuration and do not need it. Everything lives in
deploy/, and deploy/README.md is the complete guide; these pages are its map.
- Caddy is the only container with published ports. It terminates TLS for three host names, gets and renews their certificates from Let's Encrypt and forwards the client's address.
- The gateway, the identity host and the API listen on plain HTTP inside the network,
and call each other at their public
https://names. - The migration runner applies every migration on each
docker compose up, and the hosts start only once it has exited successfully. - PostgreSQL, RabbitMQ and Redis are not published.
| Host name | Serves |
|---|---|
app.example.com | The gateway: the application and its API calls, /mcp, /scim/v2, Stripe's webhook |
identity.example.com | The identity host: sign-in and the account pages |
api.example.com | The API, for scripts and agents with a personal access token |
The landing page and the documentation site are static: deploy them to any static host, or add them to the compose file (see Operate).
Try it on your machine first
The same images, in the same Production environment, on *.localhost — certificates
from a local authority, e-mail caught by MailPit, the sample users seeded:
cd deploy
cp local/local.env.example .env
./local/make-ca.sh
docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build
Import local/ca/root.crt into your browser's trusted authorities, open
https://app.localhost and sign in as J0hnd0e / Password123!.
The server
- A Linux VPS with 2 vCPU and 4 GB of RAM at least (8 GB is comfortable), amd64 or arm64, and 20 GB of disk plus your data.
- Docker Engine with the Compose plugin.
- Ports 80 and 443 open (and 443/udp for HTTP/3) — nothing else.
- Three DNS records pointing at the server, created before the first start: Caddy asks for the certificates straight away.
Next: configure it.