What the error means
Error: listen EADDRINUSE: address already in use :::3000 is Node telling you that it asked the kernel for port 3000 and the kernel said no, because a process already has it. Vite, Next.js, Rails (Address already in use - bind(2)), and Python (OSError: [Errno 48] Address already in use) say the same thing in their own words.
The process is almost never a mystery. It is yesterday’s dev server in a terminal tab you closed, the same project started twice, a Docker container publishing the port, or a tool that restarts itself. Nothing is broken. You only need to find the owner and decide what to do with it.
Find the owner with lsof
macOS ships lsof. Ask it for TCP listeners on the port:
-n and -P skip DNS and service-name lookups so the output comes back at once. -sTCP:LISTEN hides clients that merely connected to the port. A typical line:
Two things matter: the PID (48213) and the command (node). node alone does not tell you which project, so look at the full command line and the folder it runs in:
The first line shows the arguments (next dev, vite, nodemon server.js) and the TTY. A TTY of ?? means the process has no terminal any more: the tab was closed and the server kept going. The second prints the working directory, which names the project.
If lsof prints nothing but the error persists, check the address family. Some servers bind only 127.0.0.1, others ::. lsof -nP -iTCP:3000 without the state filter shows both, and sudo lsof -nP -iTCP:3000 shows listeners owned by other users, such as a system service or a Docker helper.
Stop it without reaching for kill -9
Send the normal termination signal first and give the process a moment to exit:
kill with no flag sends SIGTERM. Node, Vite, and most servers catch it, close their sockets, and exit cleanly. If the port is still listed after a second or two, and the PID is the same one, escalate:
SIGKILL cannot be caught, so the process dies at once, along with anything it was writing. Use it as the second step, not the first, and only on the PID you already looked at. Do not type the PID from memory: a different process can take the port in between, and kill -9 on the wrong PID is how terminals lose editors.
The one-line version people paste around, kill -9 $(lsof -t -i:3000), does both mistakes at once: it skips SIGTERM and kills whatever holds the port at that instant, including a client connection if you leave out -sTCP:LISTEN.
When the server comes right back
If the port is busy again a second after you freed it, something is restarting the process:
nodemon, ts-node-dev,
--watchscripts. They respawn their child on exit. Stop the parent:ps -o pid,ppid,command -p <pid>shows the parent PID, and that is the one to signal.pm2, forever, launchd.
pm2 listandpm2 stop <name>; for launchd,launchctl list | grep -i <name>andlaunchctl bootout gui/$(id -u)/<label>.Docker. A container publishing
3000:3000holds the port through a helper process, solsofshowscom.docker.backendor similar rather than your app.docker ps --filter publish=3000finds the container,docker stop <id>frees the port.The same project twice. Two terminal tabs, or the editor’s integrated terminal and your own. The second start fails, the first keeps running; find the one with a TTY and leave it, or stop both.
If you need port 3000 free permanently, most tools also let you pick another port (PORT=3001 npm run dev, vite --port 3001, next dev -p 3001).
Doing this in Room Service
Room Service runs the same lsof query for you, every two seconds while the app is in front, and shows the result as a list instead of a table of PIDs. Each row names the port, the process, the PID, and the project folder the server was started from, so :3000 reads as storefront rather than node. A server whose terminal is gone gets a Detached badge, which is the usual answer to “why is 3000 still in use after I closed the tab”.
Click Stop on a row and confirm. Room Service sends SIGTERM, checks the port four times over about a second, and force quits only if the same PID still holds it. If a different process has taken the port in the meantime, it stops nothing and tells you. From the terminal, roomservice ports prints the list and roomservice ports --kill 3000 does the same stop after a y/N prompt, with --pid <pid> for scripts that want to be sure they stop what they saw.
Seeing the list is free. Stopping a server from the app, the CLI, or an agent needs Pro.
Related
Dev Ports: every local server with its port, project, and a Stop button
