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 uprailway 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.