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