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

  1. Replace any hard-coded port with process.env.PORT || 3000.
  2. Bind to 0.0.0.0, not localhost or 127.0.0.1.
  3. Ensure your root route (or configured health path) returns 200.
  4. If startup is slow, raise the health check timeout in Service → Settings → Healthcheck.
  5. 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.

Related guides