Railway Deployments Fix 8 min read

Railway Deployment Failed? How to Fix Build, Deploy, and Health Check Errors

Troubleshoot Railway deployment failures — Nixpacks build detection, health check timeouts, PORT binding, environment variables not loading, and the difference between build failed and deploy failed.

Railway live status

Railway — live status

Updated every 5 minutes · Full incident history →

Full status →

Build failed vs Deploy failed vs Health check failed

Build failed Nixpacks could not detect your stack, install dependencies, or compile your code. Fix is in your repo — package.json, requirements.txt, Procfile.
Deploy failed Build succeeded but the container crashed at startup — uncaught exception, wrong PORT, or missing runtime env var.
Health check failed App started but Railway’s probe did not receive a 200 response in time — wrong PORT binding or missing health route.

Step-by-step fixes

1. Nixpacks build detection failing

Nixpacks scans your repo root for well-known files to detect the runtime. If it cannot identify the stack it exits with Error: no build plan found. Common requirements:

Node.js — needs a start script

{
  "scripts": {
    "start": "node server.js"
  }
}

A package.json without a start script causes Nixpacks to skip Node detection. Railway does not run npm run dev — it runs npm start.

Python — needs a dependency file and a Procfile

# requirements.txt must be in the repo root (or Pipfile / pyproject.toml)
fastapi==0.111.0
uvicorn[standard]==0.30.0

# Procfile (also in repo root)
web: uvicorn main:app --host 0.0.0.0 --port $PORT
Important: a Python script that just runs a calculation is not a web server. Railway requires an HTTP server process — if your script has no HTTP listener, the deployment will fail immediately after the container starts.

2. PORT variable — must use process.env.PORT

Railway injects PORT at runtime. Hardcoding any port causes the health check to probe the wrong port and fail.

Node.js (Express)

// Wrong — hardcoded port will fail health check
app.listen(3000);

// Correct — read PORT from environment
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

Python (FastAPI / uvicorn)

import os

# Procfile passes $PORT to uvicorn — no change needed in app code
# web: uvicorn main:app --host 0.0.0.0 --port $PORT

# If running via Python directly:
if __name__ == "__main__":
    import uvicorn
    port = int(os.environ.get("PORT", 8000))
    uvicorn.run(app, host="0.0.0.0", port=port)

3. Health check failed — add a /health endpoint

When a health check path is configured, Railway sends GET /health and expects HTTP 200 within the timeout window (default 60 s). Configure this in your service: Settings → Deploy → Health Check Path.

Node.js (Express)

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok' });
});

Python (FastAPI)

from fastapi import FastAPI

app = FastAPI()

@app.get("/health")
def health():
    return {"status": "ok"}
  • Slow startup: if your app takes more than 60 s to start (loading a large ML model, running migrations), increase the health check timeout in Settings → Deploy → Health Check Timeout.
  • No path set: if you have not configured a health check path, Railway probes /. If your root route returns anything other than 2xx, the deployment fails.

4. Environment variables not found

Railway never reads .env files from your repository. All variables must be set in the Railway dashboard or via the CLI:

# CLI (install with: npm i -g @railway/cli)
railway login
railway link          # link to your project
railway variables set DATABASE_URL=postgres://...
railway variables set SECRET_KEY=my-secret

# Dashboard path:
# Project → your service → Variables tab → + New Variable
  • Auto-redeploy: Railway automatically triggers a redeploy when you add or change a variable in the dashboard — no manual redeploy needed.
  • Railway-injected vars: Railway automatically provides PORT, RAILWAY_PUBLIC_DOMAIN, and RAILWAY_PRIVATE_DOMAIN — do not set these manually.
  • Shared variables: use Railway’s Shared Variables (project-level) to share secrets like DATABASE_URL across multiple services in the same project.

5. Private networking vs public domain

Railway gives each service two domains — a public one and a private internal one. Using the wrong one is a common source of connection errors between services in the same project:

# Public domain (accessible from the internet)
RAILWAY_PUBLIC_DOMAIN=my-app.up.railway.app

# Private domain (only works between services in the same project)
RAILWAY_PRIVATE_DOMAIN=my-app.railway.internal

# For service-to-service calls inside Railway, use the private domain:
# http://my-api.railway.internal:3000/endpoint
# This avoids egress billing and is faster than the public URL.
  • Internal port: services on the private network are reached on the port your app listens on (i.e. process.env.PORT), not port 80 or 443.
  • No public domain yet: if RAILWAY_PUBLIC_DOMAIN is not set, go to your service → Settings → Networking → Generate Domain.
🔔

Get alerts when Railway has an outage

Free email alerts. Star Railway on Prismix — no credit card needed.

FAQ

What is the difference between Railway build failed and deploy failed?

Build failed means Nixpacks could not compile or install your app — check for a missing start script or dependency file. Deploy failed means the container crashed at startup — check your PORT binding and runtime environment variables.

Why does Railway say “Health check failed”?

Railway probes your Health Check Path and expects HTTP 200. Your app must listen on process.env.PORT and respond before the timeout. Add a GET /health route returning 200 and set it as the health check path in Settings → Deploy.

How do I fix Railway environment variables not found?

Do not use .env files — Railway ignores them. Add variables in the Railway dashboard under your service → Variables tab, or with railway variables set KEY=value via the CLI. Railway automatically redeploys after a variable change.

What PORT should my app listen on for Railway?

Always use process.env.PORT (Node.js) or os.environ["PORT"] (Python). Railway injects this variable — never hardcode a port number in a Railway service.

Monitor related services