502 Bad Gateway after deploy: bind to 0.0.0.0 and $PORT
By Brook Solomon, Founder, EthioDeploy
A 502 right after a deploy almost always means the platform's proxy can't reach your app. The app is listening on 127.0.0.1 instead of 0.0.0.0, or on a hard-coded port instead of the one in the PORT environment variable. Bind to 0.0.0.0 and read PORT, and the 502 goes away.
Why does my app return 502 after deploy when it works locally?
Because the proxy in front of your app can't connect to it. On your laptop you open localhost:5000 from the same machine, so an app listening on 127.0.0.1 works fine. On a hosting platform, requests arrive from a proxy in another container or machine. An app bound to 127.0.0.1 only accepts connections from itself, so the proxy gets nothing back and returns 502.
The second cause is the port. Platforms tell your app which port to use through the PORT environment variable. An app that always listens on 5000 or 8000 is running, just not where the proxy is looking.
Every major platform documents the same rule:
- Render: “Every Render web service must bind to a port on host
0.0.0.0to serve HTTP requests.” Its defaultPORTis 10000. - Railway says the most common reason for “Application failed to respond” is that the app “is not listening on the correct host or port.”
- Heroku assigns the port as
PORTand kills a web process that hasn't bound to it within 60 seconds (error R10, which then shows up as H10 App crashed).
What host and port does my framework use by default?
Most development servers default to 127.0.0.1 on purpose, so your work-in-progress isn't reachable from the network. That default is what breaks in production.
| Server | Default host | Default port | Reads PORT? |
|---|---|---|---|
Node server.listen, Express | All interfaces | None, you pass it | Only if your code does |
Next.js next start | 0.0.0.0 | 3000 | Yes |
Vite vite preview | localhost | 4173 | No |
| uvicorn | 127.0.0.1 | 8000 | No |
| gunicorn | 127.0.0.1, or 0.0.0.0 when PORT is set | 8000 | Yes |
Flask flask run | 127.0.0.1 | 5000 | No |
Django runserver | 127.0.0.1 | 8000 | No |
Go ListenAndServe(":8080") | All interfaces | The one in the string | Only if your code does |
Rails rails server | localhost in development, 0.0.0.0 otherwise | 3000 | Yes |
How do I fix it in my framework?
Read the port from the environment and listen on all interfaces. The start commands:
# Node / Express
app.listen(process.env.PORT || 3000, "0.0.0.0")
# FastAPI, Starlette (uvicorn)
uvicorn main:app --host 0.0.0.0 --port $PORT
# Flask, Django (gunicorn binds 0.0.0.0:$PORT on its own when PORT is set)
gunicorn app:app
gunicorn myproject.wsgi
# Vite static preview
vite preview --host 0.0.0.0 --port $PORT --strictPort
# Go
http.ListenAndServe(":"+os.Getenv("PORT"), nil)Three gotchas that look like a 502 but aren't a missing host:
- Next.js can't take
PORTfrom a.envfile. Its docs say the HTTP server boots before.envis read. SetPORTin the platform's environment instead. - Vite moves to the next free port when the one you asked for is busy. Add
--strictPortso it fails loudly instead of listening somewhere else. - Django's
runserverand Flask'sflask runare development servers. Their docs say not to use them in production. Run gunicorn instead.
What happens on EthioDeploy?
EthioDeploy sets PORT=3000 and HOSTNAME=0.0.0.0 for every web service, and overrides any value you set for those two. Then every deploy waits for a health check: a GET to / that has to return a 2xx or 3xx status within 30 seconds.
If the app never answers, the deploy fails and your previous version keeps serving traffic, so a wrong bind never takes the site down. The build log says what went wrong:
Health check timed out after 30000ms. The app never answered on port $PORT. Make sure it listens on 0.0.0.0 and reads the PORT env var.
If your home page needs a login or returns 404 by design, set a Health Check Path such as /healthz in Settings. The framework guides list the exact start command for each stack: Express, FastAPI, Django, Flask and Go.
Questions
What does binding to 0.0.0.0 mean?
It tells the server to accept connections on every network interface of the machine. 127.0.0.1 only accepts connections from the same machine, so a proxy running elsewhere can't reach it.
Is listening on 0.0.0.0 a security risk?
Not inside a container on a hosting platform. The container only receives traffic the platform's proxy sends it. On your own laptop or a server with a public IP, 0.0.0.0 does expose the app to the network, which is why development servers default to 127.0.0.1.
Why does my app work locally but not after deploy?
Locally you connect from the same machine, so 127.0.0.1 and a fixed port work. After deploy the platform's proxy connects from outside the app and expects the port in the PORT environment variable.
What port should my app listen on?
The one in the PORT environment variable. Each platform picks its own: Render's default is 10000, Heroku assigns one per dyno, and EthioDeploy sets 3000. Reading PORT works on all of them.
What is Heroku error H10 App crashed?
Heroku shows H10 when a web dyno crashed or timed out while booting. A common cause is error R10: the process didn't bind to $PORT within 60 seconds of starting.
Sources
Checked on October 29, 2026: