VT Security blog

Address already in use: quién tiene el puerto y por qué kill -9 va al final

Cómo encontrar el proceso con ss, ver quién lo levantó y cortarlo sin kill -9. Y qué significa que esté escuchando en 0.0.0.0.

En este artículo
  1. Quién tiene el puerto
  2. Qué quiere decir 0.0.0.0
  3. kill manda una señal
  4. Si vuelve solo, es un servicio
  5. El criterio

Pasa así: levantás tu app, la terminal te devuelve EADDRINUSE y no arranca. Buscás el error y la primera respuesta dice kill -9 $(lsof -t -i:3000). Lo corrés y anda.

Casi siempre. A veces el puerto vuelve a estar ocupado un segundo después. Otras, lo que mataste era la base de otro proyecto que tenías abierto. Y en el camino se pasa por alto algo que importa más que el error: ese proceso que no sabías que seguía corriendo también podía estar atendiendo a otras máquinas.

Quién tiene el puerto #

El error solo dice el puerto. Para saber quién lo tiene está ss, que viene en cualquier distro actual:

terminal
$ node -e "require('http').createServer().listen(3000)"
Error: listen EADDRINUSE: address already in use :::3000
$ ss -ltnp 'sport = :3000'
State  Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0      5            0.0.0.0:3000      0.0.0.0:*    users:(("python3",pid=3365731,fd=3))

-l muestra lo que está escuchando, -t TCP, -n números en vez de nombres y -p el proceso. La última columna es la respuesta: un python3 con PID 3365731. Si esa columna sale vacía, el proceso es de otro usuario y con sudo adelante aparece.

Con el PID, ps te dice qué es y hace cuánto corre:

terminal
$ ps -o pid,ppid,user,etime,cmd -p 3365731
    PID    PPID USER         ELAPSED CMD
3365731 3365724 valen          00:01 python3 -m http.server 3000

Acá dice un segundo porque lo levanté para el ejemplo; uno olvidado suele decir días. python3 -m http.server es un clásico: lo usaste para pasar un archivo y quedó corriendo en otra terminal, sirviendo la carpeta entera donde lo levantaste, archivos ocultos incluidos.

Qué quiere decir 0.0.0.0 #

Volvé a la columna Local Address. 0.0.0.0:3000 quiere decir que el proceso acepta conexiones por cualquier interfaz de la máquina: le puede hablar cualquiera que esté en tu red. Mandá pedidos y cambiá la dirección mientras llegan.

tu redrouterinternet1 cortadootra máquina de tu redOPENAI_API_KEY=sk-…conexión rechazadase llevó el .env 1 veztu máquinatu navegador1 respuestapython3 -m http.server.envapp.js0.0.0.0:3000tu redrouterinternet1 cortadootra máquina.env descargadoconexión rechazadase llevó el .env 2 vecestu máquinatu navegador1 respuestapython3 -m http.server.envapp.js0.0.0.0:3000

Cualquiera en tu red llega al puerto y se baja el .env. Desde internet no, mientras el router no lo deje pasar.

Red de ejemplo. El router corta lo que entra de internet mientras nadie le abra el puerto.

Lo probé con ese mismo servidor, levantado en una carpeta de prueba con un .env. Desde otra máquina de la red:

terminal
$ curl -s http://<ip>:3000/.env
OPENAI_API_KEY=sk-proj-demo-no-es-real

La key es de mentira y <ip> reemplaza la de mi máquina. Levantado con --bind 127.0.0.1, el mismo curl devuelve Couldn't connect to server. Y si lo corrés en tu home, la carpeta que se sirve incluye .ssh.

El error de Node de más arriba también lo dice: :::3000. Si no le pasás una dirección, listen(3000) escucha en ::, que son todas.

kill manda una señal #

kill no mata: le manda una señal al proceso. Si no le decís cuál, manda TERM, que le pide terminar y lo deja cerrar lo que tiene abierto. kill -9 manda KILL, que el programa no puede atender: el kernel lo saca en el acto. Mandá pedidos y, cuando quieras, mandale una señal. Después levantalo de nuevo y probá la otra.

un clienteseñalappPID 43212 cambios en memoria2 cambios perdidoscerrando…salió soloel kernel lo sacódatos.db0 cambios guardadosapp.pidya está corriendo (PID 4321)un clienteseñalappPID 43212 cambios en memoria2 cambios perdidoscerrando…salió soloel kernel lo sacódatos.db0 cambios guardadosapp.pidya está corriendo (PID 4321)

Atiende pedidos y cada tanto guarda en el disco. Mandale una señal.

Programa y tiempos de ejemplo. No todos dejan un lock, pero cualquiera pierde lo que no alcanzó a escribir.
SeñalNúmeroQué pide¿La puede atender el proceso?
HUP1Varios servicios la usan para releer la configuraciónSí
INT2Es lo que manda Ctrl+CSí
TERM15Que termine: cerrar lo que tiene abierto y salirSí
KILL9Nada. El kernel lo saca en el momentoNo

El orden es kill, esperar unos segundos, y kill -9 solo si sigue ahí.

Si vuelve solo, es un servicio #

A veces matás el proceso y el puerto vuelve a estar ocupado al segundo. Lo reproduje con un servicio de systemd que se reinicia solo, y pasó exactamente eso. Mandá pedidos, matalo y mirá qué pasa con los que llegan mientras no está.

systemd --uservigila demo-web.serviceRestart=alwaysactive (running)inactivepython3 -m http.serverPID 3366803:3000ocupado por 3366803un clientesystemd --uservigila demo-web.serviceRestart=alwaysactive (running)inactivepython3 -m http.serverPID 3366803:3000ocupado por 3366803un cliente

systemd vigila el servicio. Mandá pedidos y probá matar el proceso.

Los dos primeros PID son los de la prueba real; los que siguen, de ejemplo.

El padre de ese proceso es systemd, así que matarlo no sirve. systemctl status acepta un PID y te dice de qué servicio es:

terminal
$ systemctl --user status 3366835 | head -4
● demo-web.service - [systemd-run] /usr/bin/python3 -m http.server 3000 --bind 127.0.0.1
     Loaded: loaded (/run/user/1000/systemd/transient/demo-web.service; transient)
  Transient: yes
     Active: active (running) since Fri 2026-09-25 11:21:28 -03; 1s ago

Con eso sabés qué parar: systemctl --user stop demo-web. En un servidor lo normal es un servicio del sistema, y va sin --user. systemctl disable hace que no vuelva a arrancar con la máquina.

El criterio #

Antes de matar un proceso, fijate qué es y quién lo levantó: ss -ltnp y ps te lo dicen en dos líneas. Si vuelve solo, es un servicio y se para con systemctl stop.

Y mirá la dirección en la que escucha. Si dice 0.0.0.0 y no sabés por qué, arreglá eso antes que el error.

Valentín Torassa. Hago videos de ciberseguridad en YouTube. Si encontraste un error, avisame en los comentarios y lo corrijo con una nota al pie.

Comentarios

Pasan por moderación antes de aparecer.

Lo próximo llega también por mail. Cada dos semanas, en Apuntes.