Add a host
Railway and AWS are both made from packages/deploy/src/production.ts, which says what
production is apart from any host. A new host (Fly.io, Render, Google Cloud, Kubernetes…) is
made from it too. Your coding agent can do this with the add-host skill, which follows this
page.
To run the images on a host by hand instead, without code, see Other hosts.
What production.ts says
Section titled “What production.ts says”| Export | What it is |
|---|---|
services |
api, worker, web, site: their Dockerfile, port, command, health path |
migrate |
The command to run in the API’s image before a new version starts |
apiSettings |
What the API is told that only the owner knows. The host adds the rest |
webBuildSettings |
What the web app’s build is given, for Sentry |
siteBuild() |
The site’s build arguments: its address and the app’s are baked into its pages |
Every host must give production what it needs:
- Postgres and Redis, reachable only from the services. The API takes
DATABASE_URL, orPGHOST,PGPORT,PGUSER,PGPASSWORDandPGDATABASE. Redis must never evict:maxmemory-policy noeviction. - One public address for the web app and one for the site, over HTTPS, with
http://sent on to it. The API has none: the web image passes/apito it on the private network, atAPI_URL. - The API’s settings from the host’s own secret store, never written into what is committed. An empty value in the settings file means “leave it as it is on the host”.
- The migrations before each new version, and nothing more if they fail.
- A health check on every service with a
healthPath, and the old version kept until the new one passes it. - The visitor’s address in
X-Forwarded-Forfrom a private address. Caddy in the web image believes only those. See Other hosts.
The parts of a host
Section titled “The parts of a host”Copy what AWS has, under packages/deploy/src/<host>/:
| File | Does |
|---|---|
.env.<host>.example |
At the root. The host’s own settings, then the API’s, copied from .env.aws.example |
stack.ts or similar |
The resources, made from production.ts. Nothing about a service written twice |
deploy.ts |
plan, apply, destroy: checks the settings, fills the secret store, runs the tool |
*.test.ts |
That each requirement above holds, without the host |
Then:
- Add the host to
HOSTSinpackages/deploy/src/settings.ts, its settings toHOST_SETTINGSand its rules toofTheHostinbefore.ts, sobun run deploy:check --before <host>checks them, and toNEXTininit.ts. - Add
deploy:plan:<host>,deploy:apply:<host>anddeploy:destroy:<host>to the rootpackage.json. - Write
docs/deploy/<host>.mdlike AWS: what you need, the first deploy, what the settings file controls, what happens on each release.
A host whose tool runs your file in a sandbox, as Railway’s does, may not let it import
production.ts. Then write it out there, and add a test that holds the two together, as
railway.test.ts does.
Before you call it done
Section titled “Before you call it done”bun run checkpasses, and the new tests fail if you break a requirement on purpose.- A real deploy, from an empty account, through
deploy:check --email, and a release with a migration that fails, to see the old version keep serving. deploy:destroy:<host>leaves nothing behind that costs money.