Railway · Deployment guide
Railway PORT binding & unhealthy app errors
Short answer
Railway sets PORT dynamically and probes it for health. If your app binds to a hard-coded port or 127.0.0.1, the probe fails and the deploy is marked unhealthy.
Symptoms
- Deploy succeeds but immediately enters “Unhealthy” state.
- Logs show app listening on 3000 but Railway times out the health check.
- Service restarts every ~60 seconds in a loop.
- “Application failed to respond” shown on the public Railway URL.
Common causes
- Server binds to a hard-coded port (e.g. app.listen(3000)) instead of process.env.PORT.
- Server binds to 127.0.0.1 / localhost — must bind to 0.0.0.0 to be reachable.
- Health check path doesn't return 200 (default is /).
- Worker / background-only service has no HTTP listener but is exposed as a web service.
- Long startup blocks the health check window — increase timeout in service settings.
How DeployDoc checks this
- Parses server entrypoints for hard-coded ports and 127.0.0.1 binds.
- Checks Dockerfile / Procfile for explicit port assignments that override PORT.
- Flags missing healthcheck endpoints when health-check path is non-default.
- Detects worker services accidentally exposed as web services.
Fix it manually
- Replace any hard-coded port with
process.env.PORT || 3000. - Bind to
0.0.0.0, notlocalhostor127.0.0.1. - Ensure your root route (or configured health path) returns 200.
- If startup is slow, raise the health check timeout in Service → Settings → Healthcheck.
- Redeploy and watch the logs for “listening on 0.0.0.0:$PORT”.
When to run a DeployDoc diagnosis
Run a diagnosis after any Railway deploy that goes unhealthy, or before migrating from a local dev port to production.