← Volver al blog

Guía

Docker: "bind: address already in use" en tu Mac (lsof no te va a decir qué contenedor es)

15 de julio de 20264 min de lectura

Corres docker run -p 5432:5432 postgres o docker compose up, y te aparece:

Error response from daemon: driver failed programming external connectivity on endpoint db: Bind for 0.0.0.0:5432 failed: port is already allocated

O, dependiendo de la versión de Docker:

Error starting userland proxy: listen tcp4 0.0.0.0:5432: bind: address already in use

Mismo problema, dos mensajes distintos. Algo ya está usando el puerto 5432. Tu primer instinto probablemente es correr lsof -i :5432, y en Mac, ese instinto te va a mandar por el camino equivocado.

Por qué lsof te miente en Mac

En Linux, Docker asocia los puertos a través de un proceso liviano llamado docker-proxy, y lsof lo muestra sin problema. En Mac, Docker Desktop corre los contenedores dentro de una VM liviana, y todo el networking de los contenedores pasa por el proceso backend del propio Docker Desktop. Entonces lsof -i :5432 no te muestra postgres, ni siquiera un nombre de contenedor. Muestra esto:

COMMAND      PID   USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
com.docke  1842    ben   41u  IPv4 0x...      0t0  TCP *:5432 (LISTEN)

com.docker.backend, truncado a com.docke. Eso no es el contenedor, es la capa de networking de Docker Desktop para todos tus contenedores activos. Matar ese PID no detiene el contenedor que está en conflicto, puede cortar el networking de todos ellos, y Docker Desktop lo vuelve a levantar de todas formas. Este es el punto donde la receta lsofkill que funciona para un servidor de desarrollo normal deja de servir por completo con Docker.

Encuentra al culpable real

Pregúntale a Docker directamente, no al sistema operativo:

docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"

Esto lista cada contenedor activo con su nombre, imagen y puertos publicados en una tabla. Busca 5432 (o el puerto que esté en conflicto) en la columna PORTS y vas a tener el nombre real del contenedor.

Si preferís filtrar directo en vez de buscar a ojo:

docker ps --filter "publish=5432"

Detenlo de la forma correcta

Con el nombre del contenedor:

docker stop mi-postgres-viejo

No vayas directo a docker kill, misma lógica que SIGTERM vs SIGKILL en un proceso normal: docker stop envía primero una señal de cierre ordenado (SIGTERM, y SIGKILL después de un timeout), dándole al proceso del contenedor la chance de cerrar conexiones a la base de datos y guardar escrituras pendientes. docker kill va directo a SIGKILL.

Si es un stack manejado por Compose, detén el proyecto completo en vez de un contenedor:

docker compose -p mi-proyecto down

Usar docker stop <container-id> sobre un contenedor manejado por Compose muchas veces solo hace que la política de reinicio de Compose lo vuelva a levantar, directo al mismo puerto. Detén el proyecto, no el contenedor.

Ten en cuenta que docker compose down solo alcanza a los contenedores levantados por ese proyecto Compose específico. Si el conflicto viene de otro proyecto, o de un contenedor levantado con docker run suelto, este comando no lo va a tocar — vas a tener que encontrarlo y detenerlo por separado.

Cuando es un contenedor viejo, no uno activo

A veces docker ps no muestra nada para ese puerto y el error sigue pasando. Revisa también los contenedores detenidos:

docker ps -a

Un contenedor terminado (exited) puede seguir reteniendo un mapeo de puerto hasta que se elimina. Limpia los contenedores detenidos que no necesites:

docker container prune

Si nada funciona: reinicia la VM de Docker Desktop

Si ya detuviste todos los contenedores y limpiaste los viejos, y el puerto sigue reportándose como ocupado, la propia VM de Docker Desktop puede haber quedado con un binding de proxy obsoleto. Reiniciarla limpia ese estado sin tocar los datos de tus contenedores:

killall Docker && open -a Docker

Espera a que Docker Desktop reinicie por completo antes de reintentar. Trátalo como el último recurso después de docker ps/docker stop/docker container prune, no como lo primero a probar.

Cuando ni siquiera es Docker

Si ni docker ps ni docker ps -a muestran nada en ese puerto, es un proceso normal del sistema, no un contenedor, el que está ahí. Esa es otra receta: lsof -i :5432 en el host, y luego kill, explicado paso a paso en ¿Puerto 3000 ocupado en tu Mac?.

Preguntas rápidas

El puerto está ocupado pero docker ps no muestra nada corriendo. Revisa contenedores terminados con docker ps -a, y luego docker container prune para eliminar los que no necesites. Un contenedor detenido puede seguir reteniendo su puerto hasta que se limpia.

Corrí docker compose down pero el puerto sigue ocupado. Probablemente tenés contenedores huérfanos de una versión anterior del archivo Compose. Corre docker compose down --remove-orphans.

¿Cómo encuentro rápido qué contenedor está usando un puerto? docker ps --filter "publish=PUERTO" — reemplaza PUERTO por el número. Una línea, sin buscar a ojo en una tabla.

Ve esto en vez de escribirlo

Bosun vive en tu barra de menú y muestra cada puerto abierto en tu Mac, en vivo, mapeado al proceso detrás. Un clic para matarlo, SIGTERM primero. Útil la primera vez que pasa esto. Realmente útil la quinta vez que pasa en una misma tarde.