You start your app and it dies straight away:
Error: listen EADDRINUSE: address already in use :::3000 bind() to 0.0.0.0:80 failed (98: Address already in use) Bind for 0.0.0.0:8080 failed: port is already allocated Three tools, three wordings, one meaning: something else is already listening on that port. Here's how to find out what in about a minute, and how to fix it without breaking anything else.
Step 1: find who owns the port
Ask the kernel. On any modern Linux server:
sudo ss -ltnp 'sport = :3000' You'll get a line like:
LISTEN 0 511 *:3000 *:* users:(("node",pid=48213,fd=21)) That's the process name and its PID. Two alternatives if you prefer them:
sudo lsof -i :3000 sudo fuser 3000/tcp Use sudo. Without it you often can't see processes owned by other users, and the output is empty, which makes it look like nothing is using the port.
Now match what you found to one of the cases below.
Case 1: an old copy of your own app
The most common one. A previous npm start, a nodemon you forgot in another terminal, a screen or tmux session, or a crashed run that left a child process behind.
Stop it gently first, then harder only if you must:
kill 48213 # asks the process to exit cleanly kill -9 48213 # only if it ignores the first one If the app is managed by something, stop it there instead, or it will just come back:
pm2 list && pm2 stop myapp sudo systemctl stop myapp Case 2: another web server on 80 or 443
You install Nginx and it fails on port 80 because Apache is already there (or the other way round). Some server images ship with one preinstalled.
sudo ss -ltnp 'sport = :80' sudo systemctl disable --now apache2 sudo systemctl restart nginx Use disable --now, not just stop, or it comes back on the next reboot.
Case 3: a Docker container
If ss shows docker-proxy, a container has published that port. Find it:
docker ps --filter publish=8080 Then stop that container, or docker compose down in its project folder. If two Compose projects both publish 8080:80, change one of them to another host port, like 8081:80.
Case 4: systemd is holding the socket
Some services use socket activation: systemd listens on the port and only starts the service when a connection arrives. Stopping the service isn't enough, because the socket unit still owns the port.
systemctl list-sockets sudo systemctl stop myapp.socket On Ubuntu 24.04, SSH works this way (ssh.socket), which is why changing the SSH port needs a socket reload, not just a service restart.
Case 5: it's your own app, restarting too fast
If your server exits and immediately restarts on the same port, a custom server can fail for up to a minute while the old connections finish closing. Most frameworks set the SO_REUSEADDR socket option for you, which avoids this. If you wrote your own socket server, set it yourself before you bind:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) Not the same error: "permission denied"
Error: listen EACCES: permission denied 0.0.0.0:80 That's a different problem. Ports below 1024 need root or a capability. Don't run your app as root to fix it. Run it on a high port like 3000 and put Nginx or Caddy in front on 80 and 443, which is the setup you want in production anyway.
Or just use another port
Sometimes the simplest fix is right: make the port configurable and pick a free one.
PORT=3001 npm start Then point your reverse proxy at the new port, and you're done.
Checklist
-
sudo ss -ltnp 'sport = :PORT'to find the process and PID - Old copy of your app?
kill PID, or stop it in pm2 or systemd - Apache vs Nginx?
systemctl disable --nowthe one you don't use -
docker-proxy?docker ps --filter publish=PORT - Still busy after stopping the service?
systemctl list-sockets -
EACCESis a different error: use a high port and a reverse proxy - Make the port configurable with
PORT=
More fixes
We publish fixes like this every week on the VeloxaHost blog. Running a Node app? See Node.js hosting on VeloxaHost.
Disclosure: we run VeloxaHost, a web hosting and VPS provider. Everything above works on any Linux server.

