The Container Port Binding Mistake That Breaks Almost Every First Deploy
Your app starts, the logs look fine, but the platform says it is not responding. The cause is almost always the same. Here is what is happening and how to fix it in under a minute.
You deploy your app. The build succeeds. The logs show the server starting. You click the URL the platform gave you and get a connection error, a 502, or nothing at all.
This is one of the most common first deployment failures, and the cause is almost always the same: the app is binding to the wrong address, or listening on the wrong port, or both.
What port binding actually means
When a server app starts, it listens for incoming connections on a network address. That address has two parts: the IP address it listens on, and the port number.
The IP address determines which network interfaces the app accepts connections from. localhost (which resolves to 127.0.0.1) means the app only accepts connections from the same machine. 0.0.0.0 means the app accepts connections from any network interface, including external ones.
During local development, localhost is fine. Everything is on the same machine. Your browser and your server are both on your laptop. When you deploy to a server, the platform's load balancer is not on the same machine as your app. It is trying to connect from outside. An app bound to localhost is invisible to it.
The port problem
Hosting platforms assign a port dynamically. They tell your app which port to use through an environment variable, almost always called PORT. Your app needs to read this variable and bind to that port.
If your app ignores PORT and hardcodes a port number, it starts on a port the platform is not watching. The platform tries to connect on its assigned port, gets nothing, and marks the deployment as failed.
// This will fail on most platforms
app.listen(3000)
// This is correct
app.listen(process.env.PORT || 3000)
The || 3000 fallback makes the app work both locally (where PORT is not set) and in production (where the platform sets it).
What AI tools generate
AI tools almost always hardcode both the address and the port. The generated code looks like this:
app.listen(3000, 'localhost', () => {
console.log('Server running on http://localhost:3000')
})
This is correct for local development and wrong for production. The 'localhost' argument is the binding address. Remove it entirely (Node.js defaults to 0.0.0.0 without an explicit address) or replace it with '0.0.0.0'. Replace 3000 with process.env.PORT.
The same pattern appears across frameworks. Express does it. Fastify does it. Hapi does it. The underlying behavior is the same in all of them.
Framework-specific fixes
Express:
const port = process.env.PORT || 3000
app.listen(port, '0.0.0.0')
Fastify:
await fastify.listen({ port: process.env.PORT || 3000, host: '0.0.0.0' })
AdonisJS: Set HOST=0.0.0.0 and PORT in your environment. AdonisJS reads both from environment variables automatically.
Next.js: Next.js handles port binding correctly by default and reads PORT from the environment. No manual fix needed.
NestJS:
await app.listen(process.env.PORT || 3000, '0.0.0.0')
How to diagnose it
If your deployment shows the app starting but the health check failing, check two things in the startup logs:
First, what address is the server logging? If you see Listening on http://localhost:3000 or Server running on 127.0.0.1:3000, the app is bound to localhost. External traffic cannot reach it.
Second, what port is the app using? If it is hardcoded and does not match the PORT environment variable the platform set, the platform is sending traffic to the wrong port.
Both of these are visible in the startup log lines that most frameworks print when they start successfully.
Why this is so consistent
This failure is nearly universal for first deployments because it is invisible during development. The hardcoded localhost binding works perfectly when you are testing locally. Nothing ever fails. The code ships, the app starts on the server, and the binding address becomes a problem for the first time.
Jetpacked detects hardcoded port and address bindings during analysis and fixes them before deployment. Apps that listen on 0.0.0.0 and respect the PORT environment variable are a requirement for serving traffic, and catching this before the deployment goes out means you never see the 502.
Deploy your app in minutes
Jetpacked handles Docker, HTTPS, databases, and deployments so you can focus on building.
Launch your app free