dockerredesdevopscontenedores

Redes Docker explicadas: bridge, host, puertos y comunicación entre contenedores

Cómo se comunican los contenedores: red bridge por defecto, redes custom con DNS interno, host network, publicar puertos bien y diagnosticar conectividad.

27 de agosto de 2026·8 min de lectura

"Connection refused" entre dos contenedores es el error Docker por excelencia, y casi siempre nace de no entender que cada contenedor vive en su propia red con su propio localhost. Este artículo ordena el modelo de redes completo — qué existe, cuándo usarlo y cómo depurarlo cuando falla.

El localhost trampa

Primer choque mental: dentro de un contenedor, localhost apunta al PROPIO contenedor. Tu app en el contenedor A llamando a localhost:5432 busca una base de datos dentro de sí misma — no la encuentra. La BD corre en el contenedor B, que tiene su propia pila de red aislada.

Todo el modelo de redes Docker responde a esa realidad. Las soluciones, de más a menos recomendadas:

Red bridge por defecto (y por qué evitarla)

Sin configurar nada, los contenedores corren en bridge — la red docker0 (172.17.0.0/16). Ahí los contenedores se ven por IP interna pero no por nombre: no hay DNS automático. Comunicarse exige IPs dinámicas que cambian en cada recreación... frágil hasta el absurdo.

Es la red correcta para un contenedor suelto (docker run puntual). Para stacks multi-contenedor, no.

Redes custom: DNS integrado gratis

Crear una red definida por usuario activa el servidor DNS embebido de Docker (127.0.0.11): cada contenedor resuelve los demás por nombre de contenedor:

docker network create miapp-net
docker run -d --name db --network miapp-net postgres:16-alpine
docker run -d --name app --network miapp-net -e DATABASE_URL=postgres://app:x@db:5432/app miapp

Ese hostname db en la cadena de conexión es el DNS interno resolviendo el nombre del contenedor. Con Docker Compose esto es automático: todos los servicios de un compose comparten red del proyecto y se descubren por nombre de servicio — exactamente el patrón del stack app + PostgreSQL.

Alias adicionales para flexibilidad:

services:
  db:
    networks:
      miapp-net:
        aliases: [postgres, database]

Publicar puertos vs exponerlos

Dos conceptos distintos que conviene no mezclar:

EXPOSE (en Dockerfile): documentación pura — declara qué puerto usa el proceso. No abre nada al exterior.

-p / ports (al ejecutar): publica un puerto del HOST redirigido al contenedor. -p 8080:80 = tu máquina en 8080 → contenedor 80.

Reglas prácticas que evitan sustos:

  1. Publica solo lo que debe ser alcanzable desde fuera del host (normalmente solo el reverse proxy o el propio frontend).
  2. Base de datos y servicios internos: sin publicar. Se acceden por la red interna.
  3. Si necesitas acceso local de emergencia a un servicio interno, publica atado a loopback: -p 127.0.0.1:5432:5432. Nunca 0.0.0.0 para algo privado.
  4. Dos contenedores publicando el mismo puerto host = conflicto de arranque.

Modo host: cuando el contenedor ES la máquina

--network host elimina el aislamiento: el proceso corre con la pila de red del host directamente. Sin mapeos de puertos (los escuchados son reales), rendimiento máximo de red, y también máximo riesgo: nada separa al contenedor del sistema.

Casos donde tiene sentido: proxies de altísimo rendimiento, herramientas de monitoreo que deben ver todo el tráfico. Fuera de Linux no funciona igual (Docker Desktop lo simula); úsalo como excepción justificada, nunca por comodidad.

Redes entre proyectos

Por defecto, redes son por-proyecto en Compose (proyecto_default). ¿Un Traefik global proxyando varios proyectos? Red externa compartida:

networks:
  proxy-net:
    external: true

services:
  web:
    networks: [proxy-net]
docker network create proxy-net

Todos los proyectos que se enganchen a proxy-net quedan alcanzables por Traefik sin exponer puertos individuales — el patrón estándar de self-hosting con Nginx/Traefik como puerta única.

Diagnóstico cuando falla

Kit mínimo, en orden de sospecha:

# 1. ¿Están en la MISMA red?
docker network inspect miapp-net --format '{{range .Containers}}{{.Name}} {{end}}'

# 2. ¿Resuelve el nombre?
docker exec app ping db          # o: getent hosts db

# 3. ¿Escucha el destino en 0.0.0.0 (no solo 127.0.0.1)?
docker exec db ss -tlnp

# 4. Ruta completa de paquetes entre contenedores
docker exec app nc -zv db 5432

Los tres fallos más frecuentes en la práctica: contenedores en redes distintas (cada docker run sin --network creó la suya), servicio escuchando en localhost interno en vez de 0.0.0.0, y typo en el nombre de servicio. En ese orden.

Preguntas frecuentes

¿Contenedor → máquina host? Desde Linux, IP especial host.docker.internal requiere flag --add-host=host.docker.internal:host-gateway; en Mac/Windows viene de serie. Útil para alcanzar una BD instalada nativamente durante migraciones.

¿Las redes Docker son seguras? El aislamiento por red es real pero NO sustituye autenticación: cualquier contenedor comprometido en tu red puede hablar con los demás. Minimiza redes compartidas entre proyectos no confiables.

¿IPv6? Docker soporta redes IPv6 habilitándolas explícitamente por red; sigue siendo terreno de configuración avanzada salvo que lo necesites específicamente.


Monta stacks multi-servicio con nombres y dependencias correctas usando nuestro generador de Docker Compose, gratis y sin registro.

Pruébalo sin código

Generador de Docker Compose

Stack local con presets comunes.

Abrir Generador de Docker Compose

Hecho por

Miguel Ángel Colorado Marin (MACM)

Full-Stack Developer · Guadalajara, España

Desarrollo aplicaciones web, herramientas digitales y proyectos completos — desde el diseño hasta el despliegue.

Contáctame