Cuando arrancas tu primer contenedor con Docker, la red simplemente funciona: el proceso sale a internet y tú publicas un puerto para llegar a él. El problema aparece cuando montas dos o tres servicios y descubres que uno no ve al otro por su nombre. En esta guía repaso los tipos de red que ofrece Docker, por qué la red por defecto se queda corta y cómo montar redes personalizadas que resuelven nombres solas, con un docker-compose.yml listo para copiar.

Puntos clave

  • Docker trae seis drivers de red: bridge, host, none, overlay, macvlan e ipvlan. Para un servidor con un solo host, los tres primeros cubren casi todo.
  • La red bridge por defecto conecta contenedores por IP en el rango 172.17.0.0/16, pero no resuelve nombres entre ellos.
  • Una red bridge personalizada añade un DNS interno: los contenedores se localizan por su nombre, que es justo lo que necesitas para que una app hable con su base de datos.
  • La red host elimina la capa de aislamiento y comparte la pila de red del anfitrión; la red none deja al contenedor sin red.
  • Docker Compose crea una red personalizada por proyecto de forma automática y cada servicio es accesible por su nombre de servicio.

¿Qué tipos de red ofrece Docker?

Docker gestiona la red mediante drivers intercambiables. En una instalación estándar tienes seis incorporados, y cada uno resuelve un escenario distinto:

  • bridge: el driver por defecto. Crea una red privada virtual en el anfitrión y conecta a ella los contenedores. Es lo que quieres en un servidor con un único host.
  • host: el contenedor usa directamente la pila de red del anfitrión, sin NAT ni traducción de puertos.
  • none: sin red. El contenedor solo tiene su interfaz de loopback.
  • overlay: conecta contenedores repartidos por varios anfitriones, la base de Docker Swarm.
  • macvlan e ipvlan: dan a cada contenedor una dirección MAC o IP propia en tu red física, útil para servicios que deben aparecer como un equipo más de la LAN.

Puedes verlas con un comando. Al instalar Docker (si aún no lo has hecho, sigue la guía de instalación en Ubuntu 24.04), ya existen tres redes de arranque:

docker network ls

El resultado muestra bridge, host y none. Estas tres son las que usarás casi siempre en un homelab o en un VPS.

La red bridge por defecto y sus límites

Cuando lanzas un contenedor sin indicar red, Docker lo engancha a la red bridge por defecto, asociada a la interfaz docker0 del anfitrión. Cada contenedor recibe una IP dentro de 172.17.0.0/16 y sale a internet a través de la puerta de enlace 172.17.0.1 mediante NAT. Hasta aquí, bien.

El límite aparece con la resolución de nombres. En la red por defecto, un contenedor no puede llamar a otro por su nombre: solo llega por su IP, que además cambia en cada arranque. La única forma antigua de enlazarlos era la opción --link, hoy obsoleta y desaconsejada. Por eso montar una app y su base de datos en la red por defecto es una fuente segura de dolores de cabeza.

Hay un detalle de seguridad que conviene recordar. Desde Docker Engine v28 (marzo de 2025), el motor descarta por defecto el tráfico entrante no solicitado hacia la IP interna de un contenedor salvo que hayas publicado el puerto de forma explícita. En la práctica, un puerto sin publicar dejó de ser accesible desde otros equipos de la red local, un endurecimiento que agradecerás.

Redes bridge personalizadas y DNS interno

La solución al problema de los nombres es crear tu propia red bridge. La diferencia es enorme: en una red personalizada, Docker levanta un servidor DNS interno que resuelve los nombres de los contenedores de forma automática. Si tienes un contenedor llamado db, cualquier otro contenedor de la misma red lo alcanza escribiendo db como si fuera un nombre de dominio.

Crear una red y conectar contenedores a ella es directo:

docker network create --driver bridge mi_red
docker run -d --name db --network mi_red postgres:17
docker run -d --name web --network mi_red nginx:1.29

Ahora el contenedor web puede conectarse a db:5432 sin conocer ninguna IP. Además de la resolución de nombres, una red personalizada mejora el aislamiento (solo los contenedores que conectes a ella se ven entre sí) y te permite conectar y desconectar contenedores en caliente. Para cualquier despliegue con más de un servicio, esta es la opción por defecto que deberías tomar.

La red host y la red none

El driver host quita la capa intermedia: el contenedor comparte directamente la pila de red del anfitrión. No hay NAT ni puertos publicados, porque el contenedor escucha en los puertos del propio servidor. Es útil para un servicio que necesita máximo rendimiento de red o que abre muchos puertos, como un servidor de VPN o un descubridor de dispositivos en la LAN. A cambio, pierdes aislamiento: si el contenedor abre el puerto 8080, lo abre en el anfitrión.

Un aviso importante: el driver host es nativo en Docker Engine sobre Linux. En Docker Desktop solo funciona desde la versión 4.34, hay que activarlo en los ajustes, opera a nivel 4 y únicamente con contenedores Linux. Si desarrollas en Mac o Windows, tenlo en cuenta antes de depender de él.

En el extremo contrario está none: el contenedor arranca sin ninguna interfaz de red salvo el loopback. Nadie entra y él no sale. Sirve para tareas de procesamiento puro que no deben tocar la red, como un trabajo por lotes que solo lee y escribe en un volumen montado.

Cómo conectar varios servicios en Docker Compose

Aquí es donde todo encaja. Cuando ejecutas docker compose up, Docker crea de forma automática una red bridge personalizada para el proyecto y conecta a ella todos los servicios. Cada servicio es accesible por su nombre de servicio, sin que tengas que crear la red a mano.

Este docker-compose.yml monta una app web y una base de datos PostgreSQL en dos redes segmentadas: la app está en frontend y backend, la base de datos solo en backend, y esa red interna no tiene salida a internet.

services:
  app:
    image: nginx:1.29
    restart: unless-stopped
    ports:
      - "8080:80"
    networks:
      - frontend
      - backend
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: cambia_esta_clave
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

networks:
  frontend:
  backend:
    internal: true

volumes:
  db_data:

Arráncalo con un comando y comprueba la red que se ha creado:

docker compose up -d
docker network inspect $(basename $PWD)_backend

La app llega a la base de datos como db:5432 gracias al DNS interno. Como db no está en frontend y backend está marcada como internal, la base de datos queda aislada: no publica ningún puerto y no tiene salida a internet. Solo app publica el puerto 8080. Este patrón de segmentar redes es la base para colocar después un proxy inverso como Traefik delante de la app sin exponer nunca la base de datos.

Publicar puertos frente a exponer

Es la confusión más habitual en las redes de Docker. Publicar y exponer no son lo mismo:

  • Publicar (ports: en Compose, -p HOST:CONTENEDOR en la línea de comandos) mapea un puerto del contenedor a un puerto del anfitrión. Ese puerto queda accesible desde fuera de Docker. "8080:80" significa que el puerto 80 del contenedor responde en el 8080 del servidor.
  • Exponer (expose: en Compose, EXPOSE en el Dockerfile) solo documenta que el contenedor escucha en un puerto, para consumo interno entre contenedores. No abre nada hacia el anfitrión.

Un matiz de seguridad: publicar un puerto lo enlaza a 0.0.0.0 por defecto, es decir, a todas las interfaces. Si un servicio solo debe ser accesible desde el propio servidor (por ejemplo, un panel de administración detrás de un proxy), publícalo solo en la máquina local con 127.0.0.1:8080:80 en lugar de 8080:80. Así el puerto no queda abierto a la red.

Preguntas frecuentes

¿Puedo comunicar dos contenedores por su nombre en la red bridge por defecto?

No. La red bridge por defecto no incluye resolución de nombres, así que un contenedor solo alcanza a otro por su IP, y esa IP cambia en cada arranque. Crea una red bridge personalizada con docker network create y conecta ambos contenedores a ella: entonces se localizan por su nombre gracias al DNS interno de Docker.

¿Cuándo debo usar la red host en lugar de bridge?

Usa host cuando el servicio necesite el máximo rendimiento de red o abra muchísimos puertos, como una VPN o un sistema de domótica que descubre dispositivos por la LAN. Para el resto de casos, una red bridge personalizada es más segura porque aísla el contenedor y te deja controlar qué puertos publicas.

¿Qué diferencia hay entre publicar un puerto y exponerlo?

Publicar (-p o ports:) abre el puerto hacia el anfitrión y lo hace accesible desde fuera. Exponer (EXPOSE o expose:) es solo documentación: indica en qué puerto escucha el contenedor para que otros contenedores lo usen, pero no lo abre al anfitrión. Para llegar a un servicio desde tu navegador, necesitas publicarlo.

Conclusión

Las redes de Docker se entienden mucho mejor con una regla sencilla: la red bridge por defecto vale para pruebas rápidas de un solo contenedor, pero en cuanto tienes dos servicios que deben hablar entre sí, crea una red bridge personalizada y deja que el DNS interno haga el trabajo. Con Docker Compose eso es automático, y segmentar redes (una pública y una interna) te da aislamiento gratis. El siguiente paso natural es la persistencia de datos: cómo hacer que lo que guardan tus contenedores sobreviva a un reinicio con volúmenes y bind mounts.

También disponible en inglés: Docker networking.

Fuentes

  1. Docker Docs: Networking overview
  2. Docker Docs: Bridge network driver
  3. Docker Blog: Docker Engine 28, hardening container networking
  4. moby/moby en GitHub

Ruta: Self-hosting con Docker: de cero a producción