Fixing Heroku’s Failed to Bind to $PORT Error (R10)

When Heroku restarts a Node web process after 60 seconds, the `R10` line is the clue that catches your eye. I like that it points to the listener’s deadline rather than pretending to identify the cause.

The timeout names a failed bind, but it does not explain what stopped the listener. That distinction starts with what Heroku means by the `$PORT` error.

What the Heroku $PORT error means

R10 marks a Heroku web process that failed to bind to its assigned PORT within 60 seconds, so the code identifies a missed startup deadline rather than its cause.

Startup work can also delay the listener. Heroku’s Node.js R10 guidance covers fixed ports and localhost binding, while its error-code reference says Fir-generation apps do not emit R10.

What you seeCheck next
The app reports port 3000, then the web process times outCompare the listener port with the value Heroku assigned in PORT
A stack trace appears before the timeoutFix the first startup exception before changing the listener
PORT is present but the process still times outCheck the bind address and whether startup reaches app.listen()
Heroku R10 log excerpt showing the app reports port 3000
Example Heroku logs show R10 after the app reports port 3000. The assigned PORT is not visible here, so compare both values before calling it a port mismatch.

Check the startup path before changing the port

A Heroku web process is the process that receives external HTTP traffic. Confirm that this app should serve requests before debugging its listener, since a bot or background job can be a worker without an HTTP server.

Read from the first application log through the timeout, because the earliest message can show what stopped the server.

  • Confirm the selected web command starts the file that creates the HTTP server.
  • Fix the earliest exception if the process exits before it reaches the listener.
  • Check that the server reads the uppercase PORT environment variable rather than a separate local setting.
  • For a bot that never accepts HTTP requests, use a worker process instead of adding a web listener solely to silence a port error.

Bind Express to the port Heroku assigned

For an HTTP app, make Express read the port from PORT before it opens the listener.

Install Express when the project does not include it

Add Express from the project root only when the project does not already list it as a dependency. The install does not choose a port or startup command.

npm install express

This example adds a root route for the later HTTP check without replacing your existing route handlers. Keep those handlers in your application.

Read PORT when the server starts

Heroku sets PORT for the web process. The fallback is for local development, while the explicit bind address avoids a listener restricted to localhost.

const express = require('express');
const app = express();

const port = Number(process.env.PORT || 8000);

app.get('/', (req, res) => {
  res.send('Hello, Heroku!');
});

app.listen(port, '0.0.0.0', () => {
  console.log(`Listening on port ${port}`);
});

Number converts the environment value to a port number. Express calls the callback after the listener starts, so the printed value comes from the argument passed to listen.

Heroku’s Node.js help page calls out localhost binding as a less common cause even when PORT is used. If your app sets a host elsewhere, remove that override or bind to 0.0.0.0 as shown.

Check the Procfile and startup work

The listener code only helps if Heroku starts the file that contains it. Heroku’s Procfile documentation defines process types and commands, and requires a root-level file named Procfile without an extension.

Point the web process at the server entry point

For an Express server in index.js, the web command can be:

web: node index.js
Procfile with the web process command node index.js
The web process points to the server entry file. Heroku’s Procfile reference explains why the web process, rather than a worker, receives external HTTP traffic.

If the file name or process type differs, point the Procfile to the server entry point. Its command must reach the listener shown above.

Separate a listener failure from slow startup

If the command is right, look at what happens before app.listen(). A startup exception, a build step chained into the start command, or a dependency that blocks initialization can all keep the web process from binding.

Log evidenceNext action
Exception or missing module appears firstRepair that startup failure and confirm the selected entry file
PORT exists, but the app binds only to localhostRemove the host restriction or use 0.0.0.0
Build work runs every time the server startsMove deploy-time build work out of the web startup command
The app is a bot or background worker with no HTTP routeDeclare a worker process rather than a web process

Heroku’s Node help identifies slow startup and external dependencies as possible causes, so diagnose those before treating a timeout increase as a temporary last resort.

Test the listener before redeploying

Start the server in one terminal with a test port. The request from a second terminal checks the listener and route, not the Heroku deployment.

PORT=4173 node index.js

When the server reports that it is listening, run this request in the second terminal:

curl -sS -i http://127.0.0.1:4173/
Terminal HTTP response from the Express app listening on port 4173
The local request reaches the listener on port 4173 and returns HTTP 200 with the expected response body.

I ran the sample under Node 26.7.0 and Express 5.2.1. A request to port 4173 returned HTTP 200, so the server answered on the value supplied through PORT.

Redeploy through your app’s existing release path, then read the startup logs. If the bind error returns, follow the first application error before changing the port.

Heroku $PORT troubleshooting FAQ

A populated PORT does not tell you whether Heroku started the right process or which address it bound to.

Should I set PORT in Heroku config vars?

No. Heroku assigns PORT to the web process. Read it in your server code and keep a local fallback only for development.

Why can R10 happen when PORT is set?

PORT can exist while the app binds to localhost or starts the wrong command. An earlier exception or blocked startup work can also keep it from listening.

Does every Heroku app emit R10?

No. Heroku’s error-code reference says R10 is not emitted for Fir-generation apps. Use the startup and runtime messages available for your app generation.

Snigdha Keshariya
Snigdha Keshariya

Snigdha Keshariya covers AI concepts and tools, from AI agents, RLHF, prompting, and model architectures to coding assistants and AI productivity tools.

Articles: 111