Contenido
Tutorial de redes en Docker: bridge, redes personalizadas y comunicación entre contenedores
Cuando una aplicación necesita conectarse a otros contenedores, como una base de datos o una caché, necesitas entender las redes de Docker. La idea clave es esta: si pones en la misma red personalizada los contenedores que necesitan comunicarse, podrán conectarse entre sí usando sus nombres.
Controladores de red
Una instalación nueva de Docker incluye tres redes integradas:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
a74caef4acb9 bridge bridge local
b9a49b39804b host host local
87a8a7fd2cd9 none null local
| Controlador | Descripción |
|---|---|
bridge | Valor predeterminado. El contenedor tiene su propia IP dentro de una subred virtual |
host | El contenedor usa directamente la red de la máquina host, sin aislamiento (funciona mejor en Linux) |
none | Sin red |
overlay | Abarca varias máquinas host (Docker Swarm) |
macvlan | Hace que el contenedor aparezca en la red local como si fuera una máquina física |
Limitaciones de la red bridge predeterminada
Si no especificas una red, el contenedor se conecta a la red predeterminada llamada bridge. En esta red, los contenedores no pueden conectarse usando sus nombres:
$ docker run -d --name web1 nginx:1.30
b1d3fb9024c112c5990d9ac85012cd3f5e38c114ab4409a06f960c6d8cdb496e
$ docker run --rm alpine:3.23 ping -c 1 -W 2 web1
ping: bad address 'web1'
$ docker inspect --format '{{.NetworkSettings.IPAddress}}' web1
template parsing error: template: :1:18: executing "" at <.NetworkSettings.IPAddress>: map has no entry for key "IPAddress"
$ docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web1
172.18.0.2
$ docker run --rm alpine:3.23 ping -c 1 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.058 ms
--- 172.18.0.2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.058/0.058/0.058 ms
$ docker rm -f web1
web1
- No encuentra el nombre
web1(bad address), pero sí puede conectarse a la IP172.18.0.2. - Sin embargo, Docker asigna la IP automáticamente. Puede cambiar al volver a crear el contenedor, así que no debes fijarla en la configuración.
Cambio en Docker 29: En versiones anteriores,
{{.NetworkSettings.IPAddress}}permitía obtener la IP del contenedor. Docker 29 ha eliminado este campo (de ahí eltemplate parsing erroranterior). Usa{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}en su lugar.
Por eso, en la práctica casi siempre se crea una red personalizada.
Red personalizada: comunicación por nombre
docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=secret postgres:18
$ docker network create appnet
fadfeb6028cb78335fa94cfd7536265256a914cc461caae1026b6ebb21e9d7a0
$ docker run -d --name db --network appnet -e POSTGRES_PASSWORD=secret postgres:18
227033e27591e6a1c195a6d63a141aaf4486c6285477792fe3080e1d4deeb2c0
$ docker run --rm --network appnet alpine:3.23 ping -c 2 db
PING db (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.069 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.092 ms
--- db ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.069/0.080/0.092 ms
$ docker run --rm --network appnet -e PGPASSWORD=secret postgres:18 psql -h db -U postgres -c "SELECT version();"
version
--------------------------------------------------------------------------------------------------------------------
PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
(1 row)
$ docker network inspect appnet --format "{{range .Containers}}{{.Name}} {{.IPv4Address}}{{println}}{{end}}"
db 172.19.0.2/16
- En
appnet, el DNS integrado de Docker resuelve el nombredba la IP del contenedor de la base de datos. - El cliente de PostgreSQL se conectó correctamente con
-h dby consultó la versión de la base de datos (PostgreSQL 18.6). - El contenedor de la base de datos no usa
-p, por lo que la máquina host y el exterior no pueden conectarse. Solo pueden hacerlo los contenedores de la misma red, lo que es más seguro.
La configuración de conexión de la aplicación también usa el nombre del contenedor, por ejemplo:
docker run -d --name app --network appnet -p 3000:3000 \
-e DATABASE_URL=postgres://postgres:secret@db:5432/postgres \
myapp
Ten en cuenta lo siguiente:
- Las conexiones entre contenedores usan el puerto interno del contenedor (5432), independientemente de
-p. - Solo los servicios a los que necesites acceder desde la máquina host o desde el exterior (como un sitio web) requieren
-p. - Evita usar nombres de contenedor como
weboapp, que también son palabras de dominios de nivel superior, como destino de conexión. Funcionan dentro de la misma red, pero las consultas desde fuera de ella pueden resolver a direcciones extrañas y llevarte a conclusiones equivocadas al depurar.
Unir un contenedor a varias redes y usar alias de red
$ docker network create appnet
77fadbc5633854c9e75fd9aa65325d0155ad33f5de453abac7f15f6766402bee
$ docker run -d --name app --network appnet nginx:1.30
25b4c1d92caeef5ca8765daf2f3ad6247982cacfad4914000ded8a0a455df851
$ docker network create frontend
45a9ab78fddb9f1b47a22b9ce156144286888c42eadc2b4f589986fe1f98cee0
$ docker network connect frontend app
$ docker inspect --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{$v.IPAddress}}{{println}}{{end}}' app
appnet 172.19.0.2
frontend 172.20.0.2
$ docker run -d --name search --network appnet --network-alias es nginx:1.30
43b07713e7ec032d21e37fdf690064b389f798ed26b2715e7ea18c15840647af
$ docker run --rm --network appnet alpine:3.23 getent hosts es
172.19.0.3 es es
$ docker rm -f app search
app
search
$ docker network rm appnet frontend
appnet
frontend
docker network connectañade un contenedor en ejecución a otra red. Ahoraapptiene dos IP. Esto se suele usar para aislar servicios: coloca Nginx y la aplicación en la red frontend, y la aplicación y la base de datos en la red backend; así, Nginx no puede conectarse a la base de datos.--network-alias espermite que otros contenedores de la misma red se conecten asearchusando el nombrees. Si varios contenedores usan el mismo alias, DNS devuelve varias IP.
Administrar redes
docker network create appnet
docker network ls
docker network inspect appnet # Ver los contenedores y las IP
docker network connect appnet nombre_del_contenedor # Añadir un contenedor en ejecución a la red
docker network disconnect appnet nombre_del_contenedor
docker network rm appnet
docker network prune # Eliminar las redes que no se usan
Asignación de puertos: la aplicación debe escuchar en 0.0.0.0
-p 8000:8000 hace que Docker reenvíe al puerto 8000 de la interfaz de red del contenedor el tráfico que llega al puerto 8000 del host. Por eso, la aplicación dentro del contenedor debe escuchar en 0.0.0.0 (todas las interfaces); no puede escuchar solo en 127.0.0.1 (el localhost del propio contenedor). Compara el resultado con el servidor web integrado de Python:
$ docker run -d --name localonly -p 8000:8000 python:3.14-slim python -m http.server 8000 --bind 127.0.0.1
246edb96a9affefaab188ac7d5ceec2fb103b511bdac352cca6ed3a80ae6764a
$ curl -sS http://localhost:8000
curl: (52) Empty reply from server
$ docker rm -f localonly
localonly
$ docker run -d --name allif -p 8000:8000 python:3.14-slim python -m http.server 8000 --bind 0.0.0.0
438fa44edf6aec790a7e6b2bfcdb7af33c16a14c661c50b8d9564ff52844e5e6
$ curl -s http://localhost:8000 | head -n 3
<!DOCTYPE HTML>
<html lang="en">
<head>
$ docker rm -f allif
allif
- Si solo escucha en
127.0.0.1, aunque el puerto esté asignado,curldevuelveEmpty reply from server: el tráfico llega al contenedor, pero la aplicación solo espera conexiones en el localhost del propio contenedor y no las recibe. - Al cambiar a
0.0.0.0, funciona correctamente.
Muchos servidores de desarrollo escuchan en localhost de forma predeterminada. Cuando los ejecutes en un contenedor, especifica la dirección:
flask run --host 0.0.0.0
uvicorn main:app --host 0.0.0.0
vite --host 0.0.0.0
Conectarse al host desde un contenedor
Para que un contenedor acceda a un servicio del host (por ejemplo, una base de datos que se ejecuta en el host), usa el nombre especial host.docker.internal:
$ docker run --rm alpine:3.23 getent hosts host.docker.internal
$ docker run --rm --add-host=host.docker.internal:host-gateway alpine:3.23 getent hosts host.docker.internal
172.18.0.1 host.docker.internal host.docker.internal
- Docker Desktop (Windows, macOS): admite
host.docker.internalde forma integrada. - Linux: este nombre no está disponible de forma predeterminada (el primer comando no muestra nada). Añade
--add-host=host.docker.internal:host-gateway; así se resolverá a la dirección del host en la red de Docker.
Los servicios del host también deben escuchar en una interfaz a la que pueda conectarse el contenedor, y no solo en 127.0.0.1.
Red host y red none
docker run -d --network host nginx:1.30
El contenedor usa directamente la interfaz de red del host. Si nginx escucha en el puerto 80, será el puerto 80 del host; no hace falta -p (si lo escribes, se ignorará). Ofrece el mejor rendimiento, pero elimina el aislamiento y puede competir por puertos con las aplicaciones del host. Su comportamiento en Docker Desktop no es exactamente igual que en Linux.
--network none deja el contenedor sin red, salvo la interfaz de loopback local:
$ docker run --rm --network none alpine:3.23 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
Es útil para tareas que no necesitan red o no deben conectarse a ella, como procesar archivos que no son de confianza.
Solución de problemas
Los contenedores no se conectan entre sí mediante localhost
Cada contenedor tiene su propio localhost. Si desde el contenedor app te conectas a localhost:5432, te conectas al propio app, no al contenedor de la base de datos. Usa el nombre del contenedor, por ejemplo, db:5432.
El host no puede conectarse al contenedor
Comprueba lo siguiente en orden:
- Ejecuta
docker psy comprueba si el campoPORTSmuestra0.0.0.0:8080->80/tcp. - Comprueba si la aplicación del contenedor escucha en
0.0.0.0(el mensajeEmpty reply from serveranterior es un síntoma típico). - Comprueba si el firewall del host o el grupo de seguridad de la nube permite ese puerto.
El contenedor no puede conectarse a Internet
Suele ser un problema de DNS. Puedes especificar los DNS en /etc/docker/daemon.json (en Docker Desktop, ve a Settings → Docker Engine). Después, reinicia Docker:
{ "dns": ["1.1.1.1", "8.8.8.8"] }
Al usar Docker Compose, Compose crea automáticamente una red para el proyecto, y los servicios pueden conectarse entre sí directamente usando sus nombres.
Resumen
- La red bridge predeterminada no proporciona resolución de nombres; usa una red personalizada para conectar contenedores entre sí.
- En una red personalizada, conecta los contenedores mediante sus nombres o
--network-alias. - La aplicación debe escuchar en
0.0.0.0para que funcione la asignación de puertos. - Para que un contenedor se conecte al host, usa
host.docker.internal.
Ejercicios
Ejercicio 1: crea la red lab, inicia un contenedor redis:8-alpine llamado cache y añádelo a esa red. Después, prueba la conexión desde otro contenedor con redis-cli -h cache ping.
Respuesta
docker network create lab
docker run -d --name cache --network lab redis:8-alpine
docker run --rm --network lab redis:8-alpine redis-cli -h cache ping
La salida es PONG. Consulta los comandos de red en docker network.
Preguntas frecuentes
¿Por qué localhost dentro de un contenedor no conecta con otro contenedor?
Cada contenedor tiene su propia red, y localhost apunta al propio contenedor. Para conectar con otro contenedor, pon ambos en la misma red personalizada y conéctate usando el nombre del contenedor.
¿Cómo se conecta un contenedor a un servicio que se ejecuta en la máquina host?
En Docker Desktop, usa host.docker.internal; en Linux, añade --add-host host.docker.internal:host-gateway. Consulta «Conectar un contenedor con la máquina host» en este capítulo.
¿Cómo obtengo la IP de un contenedor en Docker 29?
Se ha eliminado {{.NetworkSettings.IPAddress}}; usa docker inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" contenedor.
Actualizado 14 de octubre de 2026