Fix: Port 3000 Is Already in Use (EADDRINUSE)
Part of: React & Frontend Errors
Quick Answer
How to fix 'port 3000 is already in use', 'EADDRINUSE', and 'address already in use :::3000' errors in Node.js, React, Next.js, and other frameworks on macOS, Linux, and Windows.
The Error
You start your development server and get one of these errors:
Node.js / Express:
Error: listen EADDRINUSE: address already in use :::3000React (create-react-app):
Something is already running on port 3000.Next.js:
Port 3000 is already in use.
Use `next dev --port` to specify a different port.All of these mean the same thing: something else on your machine is already listening on port 3000.
Why This Happens
Port 3000 can only be used by one process at a time. The kernel enforces this — a bind() call on a port that is already in use returns EADDRINUSE, and Node.js surfaces that as the runtime error you see. There is no way for two processes to share the same TCP port on the same network interface unless they explicitly use SO_REUSEPORT, which dev servers do not.
The port is not a Node-specific thing either. Any process — a Python Flask server, a Java app, a Docker container with a port mapping, or even a system service — can hold port 3000 and block your dev server. That is why the troubleshooting steps below focus on identifying which process holds the port, not on something specific to your framework.
Common causes:
- A previous dev server is still running. You closed the terminal tab but the process didn’t stop.
- Another project is using the same port. You have two apps both defaulting to port 3000.
- A zombie Node.js process. The process crashed but didn’t release the port.
- A Docker container is mapping a container port to 3000 on your host machine.
- A system or background service (like a database admin panel) is bound to that port.
- macOS AirPlay Receiver has grabbed port 5000 or 7000 (relevant if you switched to those as a workaround — see history below).
A Note on Dev Server Port Conventions
The choice of 3000, 5000, 8000, and 8080 as dev defaults is older than most current frameworks. The convention dates back to the early Express era — Express’s generator scaffolds chose 3000, Rails chose 3000, and a generation of tutorials standardized on it. That choice eventually became a problem because dozens of dev tools defaulted to the same handful of ports.
macOS Monterey (2021) caused the most painful collision. Apple started binding the AirPlay Receiver service to port 5000 by default, which silently broke Flask and dozens of other Python dev servers that had used 5000 for years. macOS Ventura extended the same pattern to port 7000 in some configurations. The fix is either to disable AirPlay Receiver under System Settings > General > AirDrop & Handoff > AirPlay Receiver, or to switch your dev server to a different port. This is why current Flask documentation recommends 5001 over 5000.
Windows reserves port ranges that you cannot bind to even though nothing is actively listening. Hyper-V, WSL2, and Windows Containers reserve large ranges of dynamic ports for internal use. Run netsh interface ipv4 show excludedportrange protocol=tcp to see them. If your “free” port appears in that list, no process holds it but you still cannot bind. The workaround is to pick a port outside the reserved ranges or to use netsh int ipv4 add excludedportrange ... to carve out a specific exclusion. This is far more common on Windows 11 with WSL2 enabled.
Next.js auto-increments now. Since Next 14, next dev automatically tries the next free port (3001, 3002, …) when 3000 is taken instead of failing. The error message above still appears on older Next versions and on most other tools that have not adopted that behavior.
Fix 1: Find and Kill the Process (macOS / Linux)
Find which process is using port 3000:
lsof -i :3000You’ll see output like:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 12345 you 23u IPv6 0x1234 0t0 TCP *:3000 (LISTEN)Kill the process using its PID. First try a graceful shutdown:
kill 12345If the process doesn’t stop after a few seconds, force-kill it:
kill -9 12345One-liner — find and kill in a single command:
lsof -ti :3000 | xargs killThe -t flag makes lsof output only the PID, which is piped directly to kill. Use xargs kill -9 instead if the graceful kill doesn’t work.
Fix 2: Find and Kill the Process (Windows)
Find which process is using port 3000:
netstat -ano | findstr :3000You’ll see output like:
TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 12345The last number (12345) is the PID. Kill it:
taskkill /PID 12345 /FOne-liner using PowerShell:
Stop-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess -ForceWhy this matters: Only one process can bind to a specific port at a time. If a previous dev server is still running in the background after you closed the terminal tab, it silently holds the port. This is especially common with Node.js processes that don’t exit cleanly after a crash.
Fix 3: Use npx kill-port (Cross-Platform)
The simplest solution that works on macOS, Linux, and Windows:
npx kill-port 3000You can kill multiple ports at once:
npx kill-port 3000 3001 8080No global install required — npx downloads and runs it on the fly.
Fix 4: Change the Default Port
If you’d rather keep whatever is on port 3000 running, change your app’s port instead.
React (create-react-app):
PORT=3001 react-scripts startOn Windows (cmd):
set PORT=3001 && react-scripts startOr use cross-env for a cross-platform solution in package.json:
{
"scripts": {
"start": "cross-env PORT=3001 react-scripts start"
}
}Next.js:
npx next dev --port 3001Or in package.json:
{
"scripts": {
"dev": "next dev --port 3001"
}
}Express:
Change the port in your code:
const port = process.env.PORT || 3001;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});Then either edit the fallback value or set the environment variable:
PORT=3001 node server.jsIf process.env.PORT ends up undefined inside your app, see Fix: process.env.VARIABLE_NAME is undefined.
Vite:
npx vite --port 3001Or in vite.config.js:
export default defineConfig({
server: {
port: 3001,
},
});Still Not Working?
Multiple Node Processes Are Running
Sometimes several zombie Node processes pile up. Kill them all at once:
macOS / Linux:
killall nodeWindows (cmd):
taskkill /IM node.exe /FWarning: This kills every Node.js process on your machine, including unrelated ones.
A Docker Container Is Using the Port
Check if a Docker container has port 3000 mapped:
docker ps --format "table {{.Names}}\t{{.Ports}}" | grep 3000If you find one, stop it:
docker stop <container_name>Or remove the port mapping by changing -p 3000:3000 to a different host port (e.g. -p 3001:3000) when you run the container.
A System Process Is Using the Port
If lsof or netstat shows a system process (not Node) on port 3000, don’t kill it blindly. Check what it is first:
macOS / Linux:
lsof -i :3000Look at the COMMAND column. If it’s something like Grafana, a database admin panel, or another known service, either disable that service or change your app’s port instead (see Fix 4).
On macOS, AirPlay Receiver is known to occupy port 5000 (Monterey and later) and sometimes 7000, not 3000 — but it’s worth checking if any macOS system service is using your port. You can disable AirPlay Receiver under System Settings > General > AirDrop & Handoff > AirPlay Receiver if it conflicts with other projects.
Pro Tip: Instead of hunting down processes every time, consider changing your app’s default port. For Next.js, add
--port 3001to your dev script. For Express, read fromprocess.env.PORTwith a fallback. This avoids collisions when you run multiple projects simultaneously.
The Port Is Freed but the Error Persists
After killing the process, if you still get the error, wait a few seconds. The OS may keep the port in a TIME_WAIT state briefly. If it persists:
macOS / Linux — check if anything is still holding it:
lsof -i :3000If the output is empty and the error continues, restart your terminal or IDE. Some tools cache the port assignment.
Windows Port Is in an Excluded Range
If netstat shows nothing listening on 3000 but Node still refuses to bind, the port may sit inside a Windows-reserved range. List the reserved ranges:
netsh interface ipv4 show excludedportrange protocol=tcpIf port 3000 (or whatever port you are trying to use) appears inside one of the listed ranges, the kernel is blocking the bind even though the port is idle. WSL2 and Hyper-V are the most common culprits, because Windows reserves a wide dynamic range at boot. Either pick a port outside the listed ranges, or restart Windows to redo the reservation (the ranges are chosen dynamically and may move on restart). For a permanent carve-out:
# Run as Administrator
netsh int ipv4 add excludedportrange protocol=tcp startport=3000 numberofports=1IPv6 vs IPv4 Conflict
The error address already in use :::3000 (note the three colons) is IPv6 syntax. Some platforms — notably older Node releases on Windows — bind to IPv6 by default and refuse to start if IPv4 already holds the port (or vice versa). Force IPv4 by binding to 0.0.0.0 instead of leaving the host unset:
app.listen(3000, '0.0.0.0');This usually resolves the conflict on machines where Docker Desktop or another service holds the IPv4 side.
Dev Server Started Twice by a File Watcher
If you use nodemon, ts-node-dev, or next dev and the port frees and re-binds in a tight loop, your file watcher is restarting too quickly. The previous instance has not released the socket yet when the new one tries to bind. Add a small delay (--delay 1 for nodemon) or restart your IDE — VS Code’s auto-save sometimes triggers two restarts back-to-back. If your dev server is crashing on startup with EADDRINUSE and then a deeper Node error, see Fix: Node.js uncaught exception. If Next.js dev mode is failing in a different way after switching ports, see Fix: Next.js 500 internal server error.
Related: If you’re seeing npm dependency errors when setting up your project, see Fix: npm ERR! ERESOLVE unable to resolve dependency tree.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: ERR_CONNECTION_REFUSED (localhost refused to connect)
How to fix 'ERR_CONNECTION_REFUSED', 'localhost refused to connect', and 'This site can't be reached' errors when accessing localhost in Chrome, Firefox, and Edge. Covers dev servers, port issues, 0.0.0.0 vs 127.0.0.1, Docker port mapping, WSL2, firewalls, and more.
Fix: process.env.VARIABLE_NAME Is Undefined (Node.js, React, Next.js, Vite)
How to fix 'process.env.VARIABLE_NAME is undefined' and environment variables not loading from .env files in Node.js, React, Next.js, Vite, and Docker.
Fix: next-themes Not Working — Hydration Mismatch, Tailwind Dark Mode, FOUC, and System Preference
How to fix next-themes errors — hydration mismatch on mount, FOUC flash before theme applies, Tailwind dark: classes not switching, ThemeProvider in App Router, defaultTheme system not respected, and TypeScript types.
Fix: React 19 Actions Not Working — useActionState, useFormStatus, useOptimistic, and form action
How to fix React 19 actions errors — useActionState signature, form action vs onSubmit, useFormStatus must be in child, useOptimistic state desync, Server Actions in client components, and error handling.