Node.js EADDRINUSE: address already in use
Port already in use
Verified against Node.js docs: net.Server.listen, Node.js Errors: EADDRINUSE, POSIX bind(2) man page · Updated May 2026
> quick_fix
Some process is already bound to your target port. Find its PID with lsof -iTCP:PORT -sTCP:LISTEN, kill it, then restart. If the port lingers in TIME_WAIT after a crash, set the SO_REUSEADDR equivalent or wait 60-120 seconds.
# macOS / Linux - find what owns the port
lsof -iTCP:3000 -sTCP:LISTEN
# kill the listed PID
kill -9 <PID>
# Windows
netstat -ano | findstr :3000
taskkill /PID <PID> /FWhat causes this error
Only one socket can be bound to a given (address, port, protocol) tuple at a time. When server.listen(3000) is called and another process is already listening, the kernel rejects the bind() syscall and Node throws EADDRINUSE. The same error appears after a hard crash if the previous socket is still in TIME_WAIT.
How to fix it
- 01
step 1
Identify the process holding the port
Use lsof on macOS/Linux or netstat on Windows to find the PID. Don't guess. The port could be held by an old dev server, a container, or a system service.
lsof -iTCP:3000 -sTCP:LISTEN -n -P # COMMAND PID USER FD TYPE DEVICE NAME # node 12345 you 23u IPv6 0x... TCP *:3000 (LISTEN) - 02
step 2
Kill the offending process
kill -9 sends SIGKILL — the process cannot trap or ignore it. If kill PID without -9 works, prefer that since it lets the process clean up gracefully.
kill 12345 # SIGTERM, graceful kill -9 12345 # SIGKILL, force - 03
step 3
If port lingers in TIME_WAIT, enable SO_REUSEADDR
After a TCP socket closes, the OS holds the (ip, port) tuple in TIME_WAIT for 60-120s to handle late packets. To skip this in dev, pass exclusive: false to server.listen, or in raw net code set socket.setReuseAddress.
const server = http.createServer(app) server.listen({ port: 3000, exclusive: false }) - 04
step 4
Read PORT from env, not hardcoded
Hardcoding 3000 means every dev tool fights for the same port. Read process.env.PORT with a fallback, and let CI/Docker assign one.
const port = Number(process.env.PORT) || 3000 server.listen(port) - 05
step 5
Catch the error and exit cleanly
Listen for the 'error' event on the server before listen(). Surfacing EADDRINUSE with a clear message beats a stack trace.
server.on('error', (err) => { if (err.code === 'EADDRINUSE') { console.error(`Port ${port} is in use`) process.exit(1) } throw err })
Why EADDRINUSE happens at the runtime level
When Node's net module calls bind(2) on a TCP socket, the kernel checks its internal bind table for an existing socket on the same (address, port, protocol) tuple. If one exists, the syscall returns EADDRINUSE (errno 48 on macOS, 98 on Linux) and Node throws. The address remains held even after the original process exits if connections were established, because the kernel keeps the tuple in TIME_WAIT for 2*MSL (60-120s) to silently absorb late retransmits and prevent stale connection state from leaking into a new socket bound to the same tuple.
Common debug mistakes for EADDRINUSE
- Killing the parent shell but not the actual node process - background tasks (npm run dev with concurrently) keep running until killed by PID.
- Assuming Ctrl-C frees the port immediately, then panicking when restart fails 5 seconds later because TIME_WAIT hasn't expired.
- Hardcoding port 3000 in three different services in the same repo, then running them in parallel without remembering only one can win.
- Editing /etc/hosts or firewall rules thinking the port is blocked, when actually another local process owns it.
- Using nodemon's --watch that triggers two restarts, where the new instance starts before the old one's listener fully closes.
When EADDRINUSE signals a deeper problem
Frequent EADDRINUSE in development usually means your team is sharing one port for many services - a sign that local dev orchestration is missing. The architectural fix is a docker-compose.yml or process-compose.yaml that assigns unique ports per service, plus a .env.local with PORT=auto so each developer gets predictable bindings. In production, EADDRINUSE on deploy points at an incomplete graceful-shutdown sequence: the new container starts before the old one's connections drain. Fix with preStop hooks and longer terminationGracePeriodSeconds in Kubernetes, or proper draining in Nginx upstreams.
Frequently asked questions
Why does the port stay busy after I Ctrl-C my dev server?
Ctrl-C sends SIGINT, which triggers Node's normal exit. If your code doesn't call server.close() in a SIGINT handler, the socket may not unbind cleanly, leaving the kernel to reclaim it after a few seconds. Add a process.on('SIGINT', () => server.close(() => process.exit())) handler so the port releases immediately. The 60-120s TIME_WAIT delay only applies after established connections, not idle listeners.
What is the difference between EADDRINUSE and EACCES on bind?
EADDRINUSE means the port is already bound by another socket. EACCES means your process doesn't have permission to bind it - typically when a non-root process tries to bind ports below 1024 on Linux. Check the err.code property to distinguish. For ports under 1024, either run as root, use setcap cap_net_bind_service, or use a higher port behind a reverse proxy.
Can two Node processes share the same port?
Yes, if you use Node's cluster module. The primary process binds the listener, then forks worker processes that share the file descriptor. Round-robin scheduling distributes connections. This is how PM2 and Node's own --cluster mode achieve multi-core scaling without EADDRINUSE. Without cluster, two processes calling listen on the same port will fail the second one.
Does Docker make this error more common?
Yes - container port mappings are easy to leak. docker run -p 3000:3000 binds the host port even after the container crashes if the daemon doesn't clean up. Run docker ps -a and remove dead containers, or restart the Docker daemon. Compose stacks especially leak ports when docker-compose down is skipped between sessions.