--- title: "Redes en Docker: bridge, host, overlay y macvlan explicados" description: "Aprende cómo funcionan las redes bridge, host, overlay, macvlan y none de Docker, con ejemplos prácticos en Ubuntu 24.04 para conectar y aislar contenedores." category: "Docker Kubernetes" language: es source: https://cubepath.com/es/docs/docker-kubernetes/redes-docker-bridge-host-overlay updated: 2026-09-25 --- # Redes en Docker: bridge, host, overlay y macvlan explicados Docker conecta los contenedores a la red a través de controladores (drivers): cada uno decide si el contenedor tiene su propia pila de red, comparte la del host o se comunica con contenedores de otros servidores. Elegir bien el controlador determina cómo se descubren los servicios entre sí, qué puertos quedan expuestos y cuánto aislamiento tienes. En esta guía probarás en Ubuntu 24.04 los controladores `bridge`, `host`, `none`, `overlay` y `macvlan` con ejemplos que puedes ejecutar tal cual. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo` y añadido al grupo `docker`. - Docker Engine instalado desde el repositorio oficial de Docker (paquetes `docker-ce` y `docker-compose-plugin`). - Para la sección de overlay: un segundo servidor con Docker y conectividad privada entre ambos. Comprueba que Docker funciona: ```bash docker version --format '{{.Server.Version}}' ``` ## Resumen de los controladores de red | Controlador | Alcance | Descubrimiento por nombre | Uso típico | |---|---|---|---| | `bridge` (predeterminado) | Un host | No | Contenedores sueltos sin configurar | | `bridge` definido por el usuario | Un host | Sí | Aplicaciones de un solo servidor (el caso más común) | | `host` | Un host | No aplica | Máximo rendimiento o acceso directo a interfaces | | `none` | Un host | No aplica | Tareas sin red, aislamiento total | | `overlay` | Varios hosts | Sí | Docker Swarm, servicios repartidos entre nodos | | `macvlan` | Red física | No | El contenedor necesita IP propia en la LAN | ## Paso 1: Revisar las redes predeterminadas Al instalar Docker se crean tres redes. Lístalas: ```bash docker network ls ``` ```output NETWORK ID NAME DRIVER SCOPE 3f1c2d7a9b10 bridge bridge local 8a4e6b2c1d33 host host local c7d9e0f1a2b4 none null local ``` La red `bridge` corresponde a la interfaz `docker0` del host, un switch virtual al que cada contenedor se conecta mediante un par `veth`. Consulta su subred: ```bash docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}} {{.Gateway}}{{end}}' ``` ```output 172.17.0.0/16 172.17.0.1 ``` ## Paso 2: Entender las limitaciones del bridge predeterminado Si no indicas `--network`, el contenedor se conecta a `bridge`. Arranca dos contenedores: ```bash docker run -d --name web1 nginx:alpine docker run -d --name web2 nginx:alpine ``` Desde `web2`, intenta llegar a `web1` por su nombre: ```bash docker exec web2 ping -c 1 web1 ``` ```output ping: bad address 'web1' ``` En la red `bridge` predeterminada no hay DNS entre contenedores: solo se comunican por IP, y la IP cambia al recrear el contenedor. Por eso Docker recomienda no usarla para aplicaciones. Elimina los contenedores de prueba: ```bash docker rm -f web1 web2 ``` ## Paso 3: Crear una red bridge definida por el usuario Una red bridge propia añade un servidor DNS interno (en `127.0.0.11` dentro de cada contenedor) que resuelve los nombres de los contenedores conectados a ella. También aísla la aplicación: los contenedores de otras redes no pueden alcanzarla. Crea la red: ```bash docker network create app-net ``` Arranca un servidor web en ella y, en la misma red, un contenedor temporal que lo consulte por nombre: ```bash docker run -d --name web --network app-net nginx:alpine docker run --rm --network app-net alpine wget -qO- http://web ``` ```output Welcome to nginx! ... ``` El nombre `web` se resuelve sin configurar nada. Si necesitas controlar el rango de direcciones (por ejemplo, para evitar solapes con una VPN o una red privada), indícalo al crear la red: ```bash docker network create --subnet 10.30.0.0/24 --gateway 10.30.0.1 backend-net ``` Puedes asignar una IP fija con `--ip` a un contenedor de esa red, aunque normalmente es mejor usar el nombre: ```bash docker run -d --name cache --network backend-net --ip 10.30.0.10 redis:7-alpine ``` ### Conectar un contenedor a varias redes Un contenedor puede estar en más de una red. Es el patrón habitual para separar el frontal de la base de datos: el proxy está en la red pública y en la interna, y la base de datos solo en la interna. Conecta `web` también a `backend-net`: ```bash docker network connect backend-net web docker inspect web --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{$v.IPAddress}}{{"\n"}}{{end}}' ``` ```output app-net 172.18.0.2 backend-net 10.30.0.2 ``` Para desconectarlo, usa `docker network disconnect backend-net web`. ### Crear una red sin salida a Internet La opción `--internal` crea una red sin ruta hacia el exterior, útil para bases de datos que solo deben hablar con la aplicación: ```bash docker network create --internal db-net docker run --rm --network db-net alpine wget -qO- -T 3 http://example.com ``` El comando falla, con un error de resolución o por tiempo de espera según la versión de Docker, porque la red no tiene salida al exterior. Los contenedores conectados a `db-net` sí pueden comunicarse entre sí por nombre. ## Paso 4: Publicar puertos de forma segura Los contenedores en una red bridge no son accesibles desde fuera del host hasta que publicas un puerto con `-p puerto_host:puerto_contenedor`: ```bash docker run -d --name web-public --network app-net -p 8080:80 nginx:alpine curl -sI http://localhost:8080 | head -n 1 ``` ```output HTTP/1.1 200 OK ``` > Advertencia: Docker añade sus propias reglas de iptables para los puertos publicados y estas se evalúan antes que las de UFW. Un puerto publicado con `-p 8080:80` queda abierto a Internet aunque UFW lo bloquee. Si el servicio solo debe ser accesible desde el propio servidor (por ejemplo, detrás de Nginx en el host), publica el puerto únicamente en localhost: ```bash docker run -d --name web-local --network app-net -p 127.0.0.1:8081:80 nginx:alpine ``` Comprueba en qué direcciones escucha cada puerto: ```bash sudo ss -tlnp | grep -E ':(8080|8081)\b' ``` ```output LISTEN 0 4096 127.0.0.1:8081 0.0.0.0:* users:(("docker-proxy",pid=4120,fd=7)) LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("docker-proxy",pid=3988,fd=7)) ``` Limpia los contenedores de este paso: ```bash docker rm -f web-public web-local ``` ## Paso 5: Usar la red host Con `--network host` el contenedor no tiene pila de red propia: usa directamente las interfaces y puertos del servidor. No hay NAT ni `docker-proxy`, así que `-p` no tiene efecto. Es útil para herramientas que necesitan ver las interfaces reales (monitorización, captura de tráfico) o para servicios muy sensibles a la latencia. ```bash docker run -d --name web-host --network host nginx:alpine curl -sI http://localhost:80 | head -n 1 ``` ```output HTTP/1.1 200 OK ``` El inconveniente es que no hay aislamiento de red: el contenedor puede chocar con puertos que ya use el host (si tienes Nginx instalado en el servidor, este contenedor no arrancará) y todo lo que escuche en `0.0.0.0` queda expuesto. Elimínalo: ```bash docker rm -f web-host ``` ## Paso 6: Usar la red none La red `none` deja al contenedor solo con la interfaz de loopback. Sirve para trabajos que procesan ficheros y no deben tener ningún acceso a la red: ```bash docker run --rm --network none alpine ip -brief addr ``` ```output lo UNKNOWN 127.0.0.1/8 ::1/128 ``` ## Paso 7: Conectar contenedores entre hosts con overlay Una red overlay une contenedores de varios servidores con un túnel VXLAN, y funciona sobre Docker Swarm. Antes de empezar, permite el tráfico de Swarm **solo desde la IP privada del otro nodo** en ambos servidores. Sustituye **`other_node_ip`** por esa IP: ```bash sudo ufw allow from other_node_ip to any port 2377 proto tcp sudo ufw allow from other_node_ip to any port 7946 sudo ufw allow from other_node_ip to any port 4789 proto udp ``` El puerto 2377/tcp es la gestión del clúster, 7946 (TCP y UDP) la comunicación entre nodos y 4789/udp el tráfico VXLAN. En el primer servidor, inicializa Swarm anunciando su IP privada, **`manager_private_ip`**: ```bash docker swarm init --advertise-addr manager_private_ip ``` La salida incluye un comando `docker swarm join --token ...`. Ejecútalo en el segundo servidor y comprueba desde el manager que ambos nodos están listos: ```bash docker node ls ``` ```output ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION k2x8v1c9q0m3n4b5v6c7x8z9a * node-1 Ready Active Leader 28.4.0 p9o8i7u6y5t4r3e2w1q0a1s2d node-2 Ready Active 28.4.0 ``` Crea la red overlay. `--attachable` permite conectar también contenedores sueltos creados con `docker run`, no solo servicios: ```bash docker network create --driver overlay --attachable app-overlay ``` Despliega un servicio con tres réplicas en esa red: ```bash docker service create --name web --network app-overlay --replicas 3 nginx:alpine docker service ps web --format '{{.Name}} {{.Node}} {{.CurrentState}}' ``` ```output web.1 node-1 Running 20 seconds ago web.2 node-2 Running 20 seconds ago web.3 node-1 Running 20 seconds ago ``` Desde cualquier contenedor de la red, el nombre del servicio resuelve a una IP virtual que reparte las peticiones entre las réplicas, estén en el nodo que estén: ```bash docker run --rm --network app-overlay alpine wget -qO- http://web | grep -o '.*' ``` ```output Welcome to nginx! ``` Para cifrar el tráfico entre nodos, crea la red con `--opt encrypted`. Docker usa IPsec (protocolo ESP), así que el firewall debe permitir ese protocolo entre los nodos, y el cifrado tiene un coste de rendimiento. ## Paso 8: Dar a un contenedor una IP de la LAN con macvlan El controlador `macvlan` asigna al contenedor su propia dirección MAC e IP dentro de la red física, como si fuera otra máquina conectada al switch. Es útil para aplicaciones heredadas que esperan estar directamente en la LAN. > Importante: La mayoría de proveedores cloud, incluidos los VPS, filtran las direcciones MAC que no conocen, por lo que macvlan normalmente solo funciona en servidores dedicados o en tu propia red local. Además, por diseño, el host no puede comunicarse directamente con sus contenedores macvlan. Sustituye la subred, la puerta de enlace y la interfaz (`eth0`) por las de tu red, y reserva un rango de IP que tu servidor DHCP no reparta: ```bash docker network create --driver macvlan \ --subnet 192.168.1.0/24 \ --gateway 192.168.1.1 \ --ip-range 192.168.1.240/28 \ -o parent=eth0 \ lan-net ``` ```bash docker run -d --name legacy-app --network lan-net --ip 192.168.1.240 nginx:alpine ``` Desde otro equipo de la LAN (no desde el propio host), `http://192.168.1.240` debe responder. ## Paso 9: Definir redes en Docker Compose En la práctica, las redes se declaran en `compose.yaml`. Compose crea una red bridge por proyecto automáticamente, pero declararlas permite separar capas. Crea un proyecto de ejemplo: ```bash mkdir -p ~/compose-redes && cd ~/compose-redes nano compose.yaml ``` ```yaml services: proxy: image: nginx:alpine ports: - "8080:80" networks: - frontend - backend cache: image: redis:7-alpine networks: - backend networks: frontend: backend: internal: true ``` Aquí `proxy` está en ambas redes y publica el puerto 8080, mientras que `cache` solo es accesible desde la red `backend`, que no tiene salida a Internet. Arranca el proyecto y comprueba que `proxy` resuelve `cache`: ```bash docker compose up -d docker compose exec proxy ping -c 1 cache ``` ```output PING cache (172.21.0.2): 56 data bytes 64 bytes from 172.21.0.2: seq=0 ttl=64 time=0.091 ms ``` Compose antepone el nombre del proyecto a las redes (`compose-redes_backend`). Detén el proyecto con `docker compose down`, que también elimina sus redes. ## Solución de problemas ### Un contenedor no resuelve el nombre de otro Comprueba que ambos están en la misma red definida por el usuario, no en el `bridge` predeterminado: ```bash docker inspect web --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}' ``` ### La red de Docker choca con una red privada o VPN Si pierdes acceso a una red interna al crear redes de Docker, es porque Docker ha tomado un rango que ya usas. Define otros rangos predeterminados en `/etc/docker/daemon.json`: ```bash sudo nano /etc/docker/daemon.json ``` ```json { "default-address-pools": [ { "base": "10.200.0.0/16", "size": 24 } ] } ``` Reinicia Docker para aplicarlo. Solo afecta a las redes nuevas, así que recrea las existentes: ```bash sudo systemctl restart docker ``` ### Depurar la red desde dentro de un contenedor La imagen `nicolaka/netshoot` incluye `dig`, `curl`, `tcpdump`, `ss` y otras herramientas. Puedes unirla al espacio de nombres de red de un contenedor existente para ver exactamente lo que él ve: ```bash docker run --rm -it --network container:web nicolaka/netshoot ``` Dentro, `dig web` o `ss -tlnp` muestran la resolución DNS y los puertos desde el punto de vista del contenedor. ### No se puede eliminar una red `docker network rm` falla si algún contenedor sigue conectado. Localízalos: ```bash docker network inspect app-net --format '{{range .Containers}}{{.Name}} {{end}}' ``` ## Conclusión Has visto por qué conviene usar redes bridge definidas por el usuario en lugar de la predeterminada, cómo publicar puertos sin exponerlos de más, cuándo usar `host` o `none`, y cómo unir varios servidores con una red overlay de Swarm o dar IP de la LAN a un contenedor con macvlan. Como siguientes pasos, puedes limpiar los recursos de prueba con `docker network prune`, poner un proxy inverso como Nginx o Traefik delante de tus contenedores, o estudiar cómo Kubernetes resuelve el mismo problema con plugins CNI.