New to Rust? Grab our free Rust for Beginners eBook Get it free →
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 see | Check next |
|---|---|
| The app reports port 3000, then the web process times out | Compare the listener port with the value Heroku assigned in PORT |
| A stack trace appears before the timeout | Fix the first startup exception before changing the listener |
| PORT is present but the process still times out | Check the bind address and whether startup reaches app.listen() |

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

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 evidence | Next action |
|---|---|
| Exception or missing module appears first | Repair that startup failure and confirm the selected entry file |
| PORT exists, but the app binds only to localhost | Remove the host restriction or use 0.0.0.0 |
| Build work runs every time the server starts | Move deploy-time build work out of the web startup command |
| The app is a bot or background worker with no HTTP route | Declare 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/

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.




