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.

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:

$ 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:

$ 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.

En macOS ss no existe. lsof -i :3000 te da lo mismo, y en Linux también anda.

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.

En el artículo original esto es una figura donde mandás pedidos y cambiás la dirección mientras llegan. Con 0.0.0.0, cualquiera en tu red llega al puerto y se baja el .env. Con 127.0.0.1, solo tu navegador:

tu máquina: python3 -m http.server sirve .env y app.js escucha en 0.0.0.0:3000 tu navegador recibe la respuesta otra máquina de tu red se lleva el .env (OPENAI_API_KEY=sk-…) internet lo corta el router escucha en 127.0.0.1:3000 tu navegador recibe la respuesta otra máquina de tu red conexión rechazada internet lo corta el router 

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:

$ 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.

Un firewall o el router pueden cortar la conexión antes. En una red que no es tuya, no contaría con eso.

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.

En el artículo original hay una figura con un programa que atiende pedidos y cada tanto guarda en el disco. Le mandás una señal, lo levantás de nuevo y probás la otra. Esto es lo que pasa con cada una:

# app (PID 4321) atiende pedidos, cada tanto guarda en datos.db y tiene su lock en app.pid $ kill 4321 # TERM # deja de aceptar pedidos nuevos y termina los que tenía # guarda todo en datos.db y borra app.pid # lo que llega después rebota: no hay nadie escuchando $ kill -9 4321 # KILL # el kernel lo saca en el acto # el pedido a medio atender se corta y los cambios que tenía en memoria se pierden # app.pid queda, y sigue diciendo que ya está corriendo (PID 4321) 

Programa de ejemplo. No todos dejan un lock, pero cualquiera pierde lo que no alcanzó a escribir.

Señal Número Qué pide ¿La puede atender el proceso?
HUP 1 Varios servicios la usan para releer la configuración Sí
INT 2 Es lo que manda Ctrl+C Sí
TERM 15 Que termine: cerrar lo que tiene abierto y salir Sí
KILL 9 Nada. El kernel lo saca en el momento No

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.

En el artículo original hay una figura donde le mandás pedidos, lo matás y mirás qué pasa con los que llegan mientras no está:

# systemd --user vigila demo-web.service (Restart=always) # python3 -m http.server, con PID 3366803, tiene el puerto :3000 $ kill -9 3366803 # el proceso murió: lo que llega rebota hasta que systemd lo levanta # un segundo después hay otro PID, 3366835, y el puerto vuelve a contestar $ systemctl --user stop demo-web # systemctl stop le avisa a systemd: nadie lo relanza y todo lo que llega rebota 

Los dos PID son los de la prueba real.

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

$ 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.

Si en la columna del proceso dice docker-proxy, el puerto lo publicó un contenedor. docker ps te dice cuál, y se para con docker stop.

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.

Practicalo en el lab: Del proceso al socket: una investigación con datos de juguete

Lo publiqué primero en vtsecurity.com.ar, donde las figuras son interactivas.