What the error looks like
In a Node.js app it usually surfaces as an uncaught exception when the server starts:
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (node:net:1872:16)
code: 'EADDRINUSE', errno: -98, address: '::', port: 3000
The same condition appears as bind: address already in use in Go, OSError: [Errno 98] in Python, and port is already allocated in Docker. The cause is identical: two things want the same host:port, and the OS only lets one have it.
Why it happens
An old instance is still running
The previous deploy, a crashed-but-not-reaped process, or a forgotten node in another terminal still holds the port.
Two services want the same port
A duplicate config entry, two replicas without distinct ports, or a system service already on :80/:443.
A hot-reloader double-started
nodemon, --watch, or a test runner spawned a second process before the first released the socket.
The socket is in TIME_WAIT
A just-closed socket can linger briefly. SO_REUSEADDR (Node sets this by default) lets you rebind without waiting.
Fix it in three steps
Find the process holding the port
# Linux
ss -ltnp 'sport = :3000'
sudo lsof -i :3000
# macOS
lsof -i :3000
# Windows
netstat -ano | findstr :3000Free the port
# Linux / macOS — graceful, then forced
kill 12345
kill -9 12345 # last resort
kill -9 $(lsof -t -i :3000)
# Windows
taskkill /PID 12345 /FRestart — or change the port
If you genuinely need both, give one a different port instead of fighting over one:
PORT=3001 node server.js
In code, read the port from the environment so it's never hard-coded: const port = process.env.PORT || 3000;
Stop it happening on every deploy
If EADDRINUSE shows up on each release, the real problem isn't the port — it's that the old instance is still alive when the new one starts.
1. Shut down gracefully on SIGTERM
const server = app.listen(process.env.PORT || 3000);
process.on('SIGTERM', () => {
server.close(() => process.exit(0)); // stop accepting, drain, then exit
});
2. Let one supervisor own the port
A supervisor should stop the old instance and confirm it's gone before starting the new one — never run two unmanaged copies that both try to bind.
One owner of the port, with configured deployment controls
Infraveil is a backend operations control plane for configured managed services on your own servers. Its supervisor can help coordinate configured service start and stop paths, but port-conflict handling depends on the component and deployment configuration. Managed deployments use the configured manual, exact-hash allowlist, or automatic approval mode; automatic mode removes a practical per-release veto. Workload permissions are deployment- and component-specific and customer-reviewed, while restarts and recovery use separately configured, bounded automation. Selected events may be reported subject to component, retention, pagination, configuration, and reachability.
Frequently asked questions
What does EADDRINUSE mean?
It's an OS-level error meaning the host:port your program tried to bind to is already in use. Only one process can listen on a given port at a time, so the second one fails to start.
How do I find what is using a port?
Linux: ss -ltnp 'sport = :3000' or lsof -i :3000. macOS: lsof -i :3000. Windows: netstat -ano | findstr :3000, then tasklist /fi "pid eq <PID>".
Why does it keep happening after every deploy?
The previous instance didn't shut down before the new one started, so it's still holding the port. Handle SIGTERM to close the listener gracefully, and let a single supervisor own the port.
Is killing the process on the port safe?
Killing a stray dev process is usually fine. In production, prefer a graceful stop (SIGTERM) so in-flight requests drain first. Use kill -9 only as a last resort.
Client-side, no signup — they run in your browser.