EADDRINUSE: address already in use — cómo liberar el puerto
Respuesta rápida
Otro proceso ya está escuchando en el puerto. Encuéntralo y mátalo:
- macOS / Linux:
lsof -i :3000→kill -9 <PID> - Windows:
netstat -ano | findstr :3000→taskkill /PID <pid> /F - Cualquier SO, un solo comando:
npx kill-port 3000
…o simplemente arranca tu servidor en otro puerto. El error completo tiene esta pinta:
MAKINOTE$ yarn startyarn run v1.22.19$ next startError: listen EADDRINUSE: address already in use 0.0.0.0:3000 at Server.setupListenHandle [as _listen2] (node:net:1432:16) at listenInCluster (node:net:1480:12) at doListen (node:net:1629:7) at processTicksAndRejections (node:internal/process/task_queues:84:21) { code: 'EADDRINUSE', errno: -48, syscall: 'listen', address: '0.0.0.0', port: 3000}
Por qué ocurre
EADDRINUSE significa que la dirección y el puerto que quiere tu servidor ya están ocupados. En una máquina de desarrollo la causa habitual es un servidor de desarrollo anterior que no se cerró bien — un proceso que crasheó, un job en segundo plano suelto, o una segunda terminal que sigue ejecutándolo. Menos a menudo, otra aplicación ocupa el puerto de verdad: otro servicio, un contenedor de Docker o un demonio del sistema.
El mismo error en cualquier otra herramienta
EADDRINUSE no es cosa de Node.js. Es el sistema operativo rechazando la llamada a bind(), y cada runtime cuenta ese mismo rechazo a su manera. Si buscaste la cadena exacta que imprimió tu herramienta y acabaste aquí, esta es la tabla de traducción:
| Herramienta | Lo que imprime |
|---|---|
| Node.js | Error: listen EADDRINUSE: address already in use 0.0.0.0:3000 |
| Python | OSError: [Errno 48] Address already in use (48 en macOS, 98 en Linux) |
| netcat | nc: Address already in use, código de salida 1 |
| Docker | bind: address already in use |
| Go | listen tcp :8080: bind: address already in use |
| Playwright | http://localhost:3000 is already used, make sure that nothing is running on the port/url |
Los números del mensaje de Python son la constante errno cruda, y cambian según la plataforma — 48 en macOS y los BSD, 98 en Linux. Eso despista cuando un contenedor reproduce el mismo fallo con un número distinto al del portátil. Reproducido en macOS 26.5:
Lo imprimiera quien lo imprimiera, el diagnóstico no cambia: algo ocupa ya ese puerto. Los comandos de abajo sirven igual con cualquier herramienta.
Qué puerto es, y quién suele ocuparlo
La mitad de estos casos no son un servidor de desarrollo zombi: son dos programas distintos que usan el mismo puerto por defecto. Los que conviene saberse:
| Puerto | Dueño habitual |
|---|---|
| 3000 | Next.js, Create React App, Rails, Grafana |
| 3001 | El segundo servidor que arrancaste después de que el 3000 estuviera pillado |
| 5000 / 7000 | En macOS, ControlCenter cuando el receptor de AirPlay está activado |
| 5173 | Vite |
| 8080 | Tomcat, Jenkins y aproximadamente todo lo que se llame "HTTP alternativo" |
| 9323 | El servidor del informe HTML de Playwright |
| 5432 / 6379 | PostgreSQL / Redis — normalmente un contenedor peleando con una instalación local |
El caso del 5000 en macOS es el que más tiempo hace perder, porque no lo ocupa nada que hayas arrancado tú. El receptor de AirPlay se queda con el 5000 y el 7000, así que una app de Flask en su puerto por defecto falla en una máquina donde el desarrollador no instaló nada. lsof lo delata al instante: el dueño es ControlCenter, no node ni python. Desactívalo en Ajustes del Sistema → General → AirDrop y Handoff, o mueve tu app.
El 9323 es el de Playwright, y se porta mejor de lo que la gente espera — conviene saberlo antes de ponerse a cazar un PID. El reporter HTML sirve ahí el informe por defecto, pero trata el 9323 como puerto preferido, no obligatorio. Leyendo playwright-core/lib/server/utils/httpServer.js en Playwright 1.57:
Captura el EADDRINUSE y reintenta sin puerto, dejando que el sistema elija uno libre. Así que un informe abierto en una pestaña no rompe la siguiente ejecución: solo hace que el informe salga en un puerto distinto del esperado.
Lo que sí tumba una ejecución de Playwright es el bloque webServer de la configuración, y es otro mensaje con otro arreglo:
El error te da el arreglo: si lo que ocupa el puerto es tu propio servidor de desarrollo, que es lo normal, reuseExistingServer: true hace que Playwright lo reutilice en vez de intentar levantar un segundo.
La dirección importa tanto como el puerto
EADDRINUSE va de una dirección y un puerto, no de un puerto suelto — por eso el mensaje nombra una, como 0.0.0.0:3000 o ::1:9323.
Esa distinción es real: 127.0.0.1:3000 (loopback IPv4) y [::1]:3000 (loopback IPv6) son direcciones distintas. Un servidor atado a una no choca necesariamente con otro atado a la otra, y uno atado a 0.0.0.0 choca con ambos porque reclama todas las interfaces IPv4 de la máquina. Eso explica dos situaciones confusas:
lsof -i :3000no enseña nada y aun así tu servidor falla — mira la dirección exacta del error y comprueba la otra familia de protocolo.- Dos servidores conviven tan ricamente "en el mismo puerto", hasta que un tercero se ata a
0.0.0.0y revienta todo.
Usa lsof -nP -iTCP:3000 -sTCP:LISTEN para ver la dirección además del PID: -n y -P se saltan las resoluciones de DNS y de nombre de servicio, así que obtienes *:3000 en vez del TCP *:hbci de la salida de arriba — hbci es solo /etc/services renombrándote el puerto 3000.
Liberar el puerto en macOS y Linux
Encuentra el proceso que ocupa el puerto con lsof (en Linux también sirven ss -ltnp o fuser 3000/tcp):
MAKINOTE$ lsof -i :3000COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEnode 38091 xabier.lameirocardam 24u IPv6 0xee02297dd086ebc1 0t0 TCP *:hbci (LISTEN)
Después termínalo. Prueba primero un kill <PID> limpio, y recurre a kill -9 <PID> solo si el proceso lo ignora:
MAKINOTE$ kill -9 38091
Liberar el puerto en Windows
Lista el PID asociado al puerto y después mátalo:
netstat -ano | findstr :3000taskkill /PID 38091 /F
Un solo comando en cualquier SO
Si prefieres no buscar PIDs, kill-port lo hace por ti — sin instalar nada:
npx kill-port 3000
O usa otro puerto
Muchas veces lo más rápido es no pelear por el puerto:
PORT=3001 npm run dev # apps que leen process.env.PORTnext dev -p 3001 # Next.js
Dentro de Docker
Si el servidor corre en un contenedor, el puerto del host lo ocupa el mapeo de puertos, no un PID local — lsof apuntará a com.docker. Para el contenedor (docker ps y luego docker stop <id>) o cambia el mapeo (-p 3001:3000).
Docker lo dice de forma lo bastante distinta como para que la gente no lo relacione con EADDRINUSE:
port is already allocated es la contabilidad interna de Docker — el demonio sabe que ya publicó ese puerto —, mientras que bind: address already in use es el kernel rechazando, igual que en todo lo demás. El primero puede sobrevivir a un contenedor que ya terminó: docker ps -a y borrar el contenedor muerto lo arregla, y docker compose down es la versión bruta.
Cuando nada de esto funciona
Dos casos en los que el puerto no es tuyo para cogerlo:
- Los puertos por debajo de 1024 necesitan privilegios. Atarse al 80 o al 443 como usuario normal falla con
EACCES, no conEADDRINUSE. Otro error, otro arreglo: si leesEACCES, deja de buscar un proceso al que matar. - El puerto está en
TIME_WAIT. Después de cerrar un socket, el sistema retiene el par hasta un par de minutos.lsof -i :3000enseña una conexión pero ningún listener, y no hay nada que matar. Los servidores lo evitan conSO_REUSEADDR, que la mayoría de frameworks ya ponen por ti; esperar también funciona.
