Deploy Error Decoder

EADDRINUSE: address already in use — how to fix it

Quick answer: EADDRINUSE means another process is already listening on the port your app is trying to bind. Find it with lsof -i :3000 (or ss -ltnp on Linux), stop that process or choose a free port, then restart. On redeploys it's almost always an old instance that never shut down — kill it, or let one supervisor own the port.

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

1

Find the process holding the port

# Linux
ss -ltnp 'sport = :3000'
sudo lsof -i :3000
# macOS
lsof -i :3000
# Windows
netstat -ano | findstr :3000
2

Free the port

# Linux / macOS — graceful, then forced
kill 12345
kill -9 12345        # last resort
kill -9 $(lsof -t -i :3000)
# Windows
taskkill /PID 12345 /F
3

Restart — 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;

The recurring case

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.

How Infraveil handles this

One owner of the port, every deploy gated and recorded

Infraveil is a backend operations control plane that runs on your own servers. Its agent is the single supervisor that starts and stops your services — so a port conflict from a half-dead process gets caught and recovered instead of crashing your release. Every deploy, restart, and recovery pauses for your approval, runs with least-privilege access, and lands in a tamper-evident audit trail with one-click rollback.

Health checks restart crashed services and clear stale processes before the new one binds
Deploys are verified and approval-gated — nothing runs until it passes
One-click rollback to the last known-good state when a release misbehaves

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.