Deploy / Railway

Railway

A persistent container, a managed Postgres, and a health check — the shortest path from consume() to production.

{
  "$schema": "https://railway.com/railway.schema.json",
  "build": { "builder": "DOCKERFILE", "dockerfilePath": "Dockerfile" },
  "deploy": {
    "healthcheckPath": "/health",
    "restartPolicyType": "ON_FAILURE"
  }
}

Save as railway.json beside the Dockerfile. Railway injects PORT; health.ts already reads it.

railway login
railway init
railway up

railway up builds the Dockerfile and streams logs until the health check passes.

railway add --database postgres
railway variables --set 'DATABASE_URL=${{Postgres.DATABASE_URL}}'

Reference syntax — not a literal — so a rotated password follows automatically. Already have a Postgres? Set DATABASE_URL to its connection string and skip railway add.

railway variables --set "SL_API_KEY=sk-sl_…"

Only needed below the free 24-hour window. The first boot sweeps history, then holds the tip in the same process — one service, both phases.

Watch the first sweep

railway logs tails the per-batch progress line. curl https://<your-app>.up.railway.app/health returns blocks_behind, which falls to 0 when the backfill catches the tip.

Railway sends SIGTERM, then kills after a grace period. The shutdown handler commits the in-flight batch and exits well inside it, so the next boot resumes from the committed cursor.