Deploying the server on Railway
The API (apps/api) runs on Railway as a Docker image. Its build and deploy settings are in
apps/api/railway.json and
apps/api/Dockerfile. Which variables decide test or real money is in
Going live.
On this page
What triggers a build
Railway redeploys on every commit to the watched branch that touches a path in watchPatterns:
apps/api/**- the packages the API links:
packages/domain,packages/merchant-contracts,packages/public-api-contracts,packages/command-types Dockerfileandrailway.json
Front-end, contract and documentation changes do not rebuild the server.
Auto-deploy should follow main for production, not a feature branch: every push to a watched
branch is a full build.
The build
- Base image:
oven/bun:1-slim, the official Bun image. - Build context: the repository root, not
apps/api, because the API imports workspace packages (@clear/domainfor refund figures, the merchant and/v1API contracts, the Command Center's types). The Dockerfile copies only those packages andapps/api. - Install: the linked packages first (so a contract change invalidates the install layer), then
bun installinapps/api. It runs without--frozen-lockfile, becausebun.lockis not in step withpackage.json. - Health check:
GET /healthevery 30 seconds, made by Bun itself. - Start:
bun src/index.ts, on port3001, restarting on failure up to 10 times.
Why it runs the TypeScript entry, not a bundle
bun build fails in a clean install: @coinbase/cdp-sdk imports @x402/svm/exact/client, which
does not resolve. The services that use the CDP SDK load it lazily, and
bun build src/index.ts --outdir dist --target bun --external "@coinbase/cdp-sdk"builds, but an external package must still be present at runtime, so the image would need
node_modules for that dependency. Until that is resolved, the image runs the source.
Replicas
The API's background jobs start in every process. Some take a database lock so only one replica
does the work; others do not. Check the jobs in apps/api/src/jobs before running more than one
replica.
Environment flags
Each defaults to the lighter setting.
PROTOCOL_EVENT_LISTENERon when T-Deeds or bonds need it.PORTFOLIO_SNAPSHOTSDATA_CHAIN_IDSMEMORY_MONITORoff silences it.POSTGRES_POOL_MAXReading the memory logs
[memory] rss=114MB heap=42/96MB external=8MBheap is used over total.
rssflat, heap used rising and falling — healthy; the collector is keeping up- the lowest heap-used figure climbing across samples — a leak; watch the floor, not the peaks
rssfar above heap total — growth outside the JavaScript heap: buffers, sockets, native code- heap used near heap total and staying there — collector pressure, which shows up as CPU, not memory
Known limitations
railway upfrom the CLI uploads what.railwayignoreallows, which leaves outpackages/command-types, a package the Dockerfile copies.