Extrañezas de red en Docker Desktop: acceso LAN, puertos y soluciones DNS que realmente funcionan

¿Te fue útil?

Ejecutas docker run -p 8080:80, visitas localhost:8080 y funciona. Le pasas la URL a un compañero en la misma Wi‑Fi y… nada.
O tu contenedor puede hacer curl a Internet pero no alcanza el NAS en tu LAN. O el DNS cambia aleatoriamente cada vez que tu VPN se conecta.

La red de Docker Desktop no está “rota”. Simplemente no es el modelo de red del host Linux que crees estar usando.
Es una VM, NAT, un montón de adaptadores específicos de plataforma y un puñado de nombres especiales que existen más que nada para salvarnos la cordura.

El modelo mental: por qué Docker Desktop es diferente

En Linux, Docker normalmente conecta los contenedores a una red puente en el host, usa iptables/nftables para NAT del tráfico saliente
y añade reglas DNAT para los puertos publicados. Tu host es el host. El kernel que ejecuta los contenedores es el mismo kernel que ejecuta tu shell.

Docker Desktop en macOS y Windows es diferente por diseño. Ejecuta una pequeña VM Linux (o un entorno Linux vía WSL2),
y los contenedores viven detrás de una frontera de virtualización. Esa frontera explica por qué la “red host” se comporta de forma extraña,
por qué el acceso LAN no es simétrico y por qué la publicación de puertos puede sentirse como orientada solo a localhost.

Piensa en capas:

  • Tu sistema operativo físico (macOS/Windows): tiene tu interfaz Wi‑Fi/Ethernet, tu cliente VPN y tu firewall.
  • La VM de Docker / WSL2: tiene su propia NIC virtual, su propia tabla de enrutamiento, sus propios iptables y su propio comportamiento de DNS.
  • Redes de contenedores: puentes dentro de ese entorno Linux; tus contenedores raramente tocan la LAN física directamente.
  • Adaptador de publicación de puertos: Docker Desktop reenvía puertos desde el OS host a la VM y luego al contenedor.

Así que cuando alguien dice “el contenedor no puede alcanzar la LAN”, tu primera respuesta debería ser: “¿Qué capa no puede alcanzar a qué capa?”

Hechos interesantes y breve historia (lo que explica el dolor actual)

  1. El modelo de red original de Docker asumía Linux. Docker popularizó el patrón “puente + NAT + iptables” porque Linux lo hacía fácil y portátil.
  2. macOS no puede ejecutar contenedores Linux de forma nativa. Docker Desktop en macOS siempre ha dependido de una VM Linux porque los contenedores necesitan funciones del kernel Linux (namespaces, cgroups).
  3. Windows tuvo dos épocas. Primero llegó Docker Desktop basado en Hyper‑V; luego WSL2 se convirtió en la ruta predeterminada para mejor comportamiento de sistema de archivos y recursos, con peculiaridades de red diferentes.
  4. host.docker.internal existe porque “el host” es ambiguo. Dentro de un contenedor, “localhost” es el contenedor; Docker Desktop necesitó un nombre estable para “el sistema host”.
  5. Los puertos publicados no son solo reglas iptables en Desktop. En Linux lo son; en Desktop a menudo se implementan mediante un proxy/reenviador en espacio de usuario a través de la frontera VM.
  6. Los clientes VPN adoran reescribir tu DNS y rutas. Con frecuencia instalan un nuevo servidor DNS, bloquean DNS dividido o añaden una interfaz virtual con prioridad sobre el Wi‑Fi.
  7. La seguridad de endpoints corporativos con frecuencia inyecta un proxy local. Esto puede romper el DNS de contenedores, MITM TLS o desviar silenciosamente el tráfico hacia infraestructura de inspección.
  8. ICMP te puede engañar en redes virtuales. “No se puede hacer ping” no significa de forma fiable “no se puede conectar”, especialmente cuando los firewalls bloquean ICMP pero permiten TCP.

Broma #1: la red de Docker Desktop es como un organigrama: siempre hay una capa más de la que crees, y nunca es la capa responsable.

Guía de diagnóstico rápido (comprueba primero/segundo/tercero)

La forma más rápida de ganar es dejar de adivinar. Diagnostica en este orden, porque aisla capas con el menor esfuerzo.

1) ¿Es un problema de publicación de puertos o de enrutamiento/DNS?

  • Si localhost:PORT funciona en tu máquina pero clientes de la LAN no pueden alcanzarlo, probablemente sea firewall del host/dirección de enlace/filtrado de rutas por VPN.
  • Si los contenedores no pueden resolver nombres o alcanzar cualquier host externo, empieza por DNS y el enrutamiento saliente dentro del contenedor/VM.

2) Identifica dónde muere el paquete (host OS → VM → contenedor)

  • Desde el OS host: ¿puedes alcanzar el objetivo de la LAN?
  • Desde dentro de un contenedor: ¿puedes alcanzar el mismo objetivo de la LAN por IP?
  • Desde dentro de un contenedor: ¿puedes resolver el nombre?

3) Verifica la dirección de enlace/escucha real y el reenviador

  • ¿El servicio escucha en 0.0.0.0 dentro del contenedor, o solo en 127.0.0.1?
  • ¿Docker publica el puerto en todas las interfaces o solo en localhost?
  • ¿El firewall del host está bloqueando entradas desde la LAN?

4) Comprueba VPN y comportamiento de anulación de DNS temprano

  • Si el problema aparece/desaparece con la VPN, deja de tratarlo como un bug de Docker. Es política, rutas, DNS o inspección.

5) Solo entonces modifica la configuración de Docker Desktop

  • Cambiar servidores DNS o rangos de red puede ayudar, pero hazlo con evidencia. Si no, solo crearás un nuevo misterio.

Tareas prácticas: comandos, salidas y decisiones (12+)

Estas son las comprobaciones que realmente ejecuto. Cada una incluye: comando, salida de ejemplo, qué significa y qué decisión tomar después.
Los comandos se muestran con un prompt genérico; adapta los nombres de interfaces e IPs a tu entorno.

Task 1: Confirmar qué contexto de Docker estás usando

cr0x@server:~$ docker context ls
NAME                DESCRIPTION                               DOCKER ENDPOINT
default *           Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
desktop-linux       Docker Desktop                            unix:///Users/me/.docker/run/docker.sock

Significado: Si crees que estás hablando con Desktop pero estás en un daemon remoto (o viceversa), todas las suposiciones de red serán incorrectas.
Decisión: Si el contexto marcado con * no es el que esperas, cámbialo: docker context use desktop-linux.

Task 2: Inspeccionar la IP de un contenedor y su adjunción de red

cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Ports}}'
NAMES          PORTS
web            0.0.0.0:8080->80/tcp
db             5432/tcp
cr0x@server:~$ docker inspect -f '{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}} {{.Gateway}}{{end}}' web
/web 172.17.0.2 172.17.0.1

Significado: El contenedor vive en un puente interno (aquí 172.17.0.0/16). Eso no es tu LAN.
Decisión: Si intentas alcanzar 172.17.0.2 desde otro portátil en la Wi‑Fi, detente. Publica un puerto o usa un patrón de red diferente.

Task 3: Comprobar en qué dirección escucha realmente tu servicio

cr0x@server:~$ docker exec -it web sh -lc "ss -lntp | head -n 5"
State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
LISTEN 0      4096   0.0.0.0:80         0.0.0.0:*     users:(("nginx",pid=1,fd=6))

Significado: Escuchar en 0.0.0.0 es bueno; acepta tráfico desde la red del contenedor.
Si ves 127.0.0.1:80, la publicación de puertos “funcionará” de formas confusas o fallará por completo.
Decisión: Si está enlazado a localhost, corrige la configuración de la app: enlaza a 0.0.0.0.

Task 4: Verificar los enlaces de puertos publicados en el lado de Docker

cr0x@server:~$ docker port web
80/tcp -> 0.0.0.0:8080

Significado: Docker cree que publicó en todas las interfaces.
Decisión: Si muestra 127.0.0.1:8080, los clientes LAN no lo alcanzarán. Re‑ejecuta con -p 0.0.0.0:8080:80 (o corrige tu archivo compose).

Task 5: Confirmar que el OS host está escuchando en el puerto esperado

cr0x@server:~$ ss -lntp | grep ':8080'
LISTEN 0      4096      0.0.0.0:8080     0.0.0.0:*    users:(("com.docker.backend",pid=2314,fd=123))

Significado: En Desktop, a menudo ves el proceso backend de Docker escuchando, no el PID del contenedor. Eso es normal.
Decisión: Si nada está escuchando, tu publicación no se aplicó o otro proceso se llevó el puerto.

Task 6: Probar desde el OS host para confirmar que el camino de reenvío funciona

cr0x@server:~$ curl -sS -D- http://127.0.0.1:8080/ | head
HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Sat, 03 Jan 2026 09:12:52 GMT
Content-Type: text/html

Significado: El reenvío de puerto host→contenedor funciona localmente.
Decisión: Si los clientes LAN no pueden conectar, céntrate en firewall/VPN/enlace-a-localhost, no en la app del contenedor.

Task 7: Probar desde un par LAN (simúlalo con otro namespace/host si puedes)

cr0x@server:~$ nc -vz 192.168.1.50 8080
Connection to 192.168.1.50 8080 port [tcp/http-alt] succeeded!

Significado: El puerto es alcanzable desde la LAN.
Decisión: Si falla con “timed out” probablemente tengas problemas de firewall/enrutamiento. Si “refused”, algo está escuchando pero no acepta en esa interfaz o el reenviador no está enlazado correctamente.

Task 8: Comprobar la configuración DNS del contenedor

cr0x@server:~$ docker exec -it web sh -lc "cat /etc/resolv.conf"
nameserver 192.168.65.5
search localdomain
options ndots:0

Significado: Docker Desktop con frecuencia inyecta una IP de resolvedor stub (ejemplo: 192.168.65.5) dentro de la red de la VM.
Decisión: Si ese nameserver es inalcanzable o se comporta mal (común con VPNs), anula el DNS a nivel de daemon/compose.

Task 9: Probar resolución DNS dentro del contenedor (no adivines)

cr0x@server:~$ docker exec -it web sh -lc "getent hosts example.com | head -n 2"
2606:2800:220:1:248:1893:25c8:1946 example.com
93.184.216.34 example.com

Significado: El DNS funciona lo suficiente como para resolver AAAA y A.
Decisión: Si se cuelga o no devuelve nada, tienes un problema en la ruta DNS. Siguiente paso: intenta resolver usando un servidor específico (si tienes herramientas instaladas) o anula los resolvedores.

Task 10: Probar conectividad por IP directa a un recurso LAN desde dentro del contenedor

cr0x@server:~$ docker exec -it web sh -lc "nc -vz 192.168.1.10 445"
192.168.1.10 (192.168.1.10:445) open

Significado: El enrutamiento contenedor → VM → host OS → LAN funciona para ese destino.
Decisión: Si la IP funciona pero el nombre falla, es DNS. Si ninguno funciona, es enrutamiento/VPN/política.

Task 11: Comprobar la ruta por defecto del contenedor (básico pero decisivo)

cr0x@server:~$ docker exec -it web sh -lc "ip route"
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 scope link  src 172.17.0.2

Significado: El contenedor enruta hacia la puerta de enlace del puente. La puerta de enlace decide luego cómo alcanzar tu LAN/Internet.
Decisión: Si falta la ruta por defecto o es incorrecta, has creado una configuración de red personalizada; vuelve atrás y prueba con una red de puente estándar.

Task 12: Comprobar si estás colisionando con una subred corporativa/VPN

cr0x@server:~$ ip route | head -n 12
default via 192.168.1.1 dev wlan0
10.0.0.0/8 via 10.8.0.1 dev tun0
172.16.0.0/12 via 10.8.0.1 dev tun0
192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.50

Significado: Si tus redes Docker usan 172.16.0.0/12 y tu VPN también enruta 172.16.0.0/12, has creado un enrutamiento ambiguo.
Desktop es especialmente sensible al solapamiento porque ya está haciendo NAT.
Decisión: Cambia los rangos de subred internos de Docker para evitar solapamientos con rutas corporativas.

Task 13: Inspeccionar redes Docker y sus subredes

cr0x@server:~$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a1b2c3d4e5f6   bridge    bridge    local
f1e2d3c4b5a6   host      host      local
123456789abc   none      null      local
cr0x@server:~$ docker network inspect bridge --format '{{(index .IPAM.Config 0).Subnet}}'
172.17.0.0/16

Significado: Ahora sabes qué subredes está consumiendo Docker.
Decisión: Si esto se solapa con rutas VPN o tu LAN, muévela.

Task 14: Validar que el contenedor puede alcanzar el OS host mediante el nombre especial de Docker Desktop

cr0x@server:~$ docker exec -it web sh -lc "getent hosts host.docker.internal"
192.168.65.2    host.docker.internal

Significado: El mapeo especial existe y apunta al endpoint del host que Docker proporciona.
Decisión: Si este nombre no resuelve, estás en una configuración antigua, un modo de red personalizado o algo alteró el DNS dentro del contenedor. Usa IPs explícitas solo como último recurso.

Patrones de acceso LAN: qué funciona y qué es engañoso

Hay tres solicitudes comunes:

  • LAN → tu servicio en contenedor (un compañero quiere acceder a tu servidor de desarrollo).
  • Contenedor → recurso LAN (el contenedor necesita acceder a un NAS, impresora, API interna, Kerberos, lo que sea).
  • Contenedor → OS host (el contenedor llama a un servicio que se ejecuta en tu portátil).

Patrón A: LAN → contenedor vía puertos publicados (el único predeterminado sensato)

Publica puertos en el OS host, no intentes repartir IPs de contenedor.
Con Docker Desktop no puedes tratar las IPs de contenedor como enrutable en la LAN física. Viven detrás de NAT, dentro de una VM, y detrás de otro NAT si tu OS también está haciendo algo inteligente.

Qué hacer:

  • Enlaza a todas las interfaces: -p 0.0.0.0:8080:80 o en Compose "8080:80" y asegúrate de que no se publique por defecto solo en localhost.
  • Abrir el firewall del host para ese puerto (y limitar el alcance; no expongas tu base de datos de desarrollo al Wi‑Fi de Starbucks).
  • Si tu VPN prohíbe entradas desde la LAN mientras está conectada, acepta la realidad: prueba sin VPN o usa un entorno de desarrollo apropiado en otro lugar.

Patrón B: contenedor → recursos LAN (el enrutamiento funciona hasta que deja de hacerlo)

Los contenedores alcanzan tu LAN generalmente por defecto, porque Docker Desktop hace NAT del tráfico saliente a través del OS host.
Luego conectas una VPN y el OS host cambia DNS y rutas. De repente tu contenedor no puede resolver o no puede alcanzar subredes que ahora están “propiedad” de la VPN.

Cuando falla, falla de algunas maneras repetibles:

  • Solapamiento de subred: Docker elige un rango privado que tu VPN enruta. Los paquetes desaparecen en el túnel.
  • Desajuste de DNS dividido: el host resuelve nombres internos vía DNS corporativo, pero los contenedores están atascados en un resolvedor stub que no reenvía dominios divididos correctamente.
  • Política de firewall: el endpoint corporativo niega tráfico desde interfaces virtuales “desconocidas”.

Patrón C: contenedor → servicios del OS host (usa los nombres especiales)

Usa host.docker.internal. Para eso sirve.
No es elegante, pero es estable frente a cambios de DHCP y menos frágil que codificar 192.168.x.y.

Si estás en Linux (no Desktop) puede que no lo tengas; en Desktop generalmente sí.

Puertos: publicación, direcciones de enlace y por qué los compañeros no pueden alcanzar tu servidor de desarrollo

Los puertos publicados son la moneda de “hacer que mi contenedor sea alcanzable”. Todo lo demás es deuda.

Localhost no es una virtud moral, es una dirección de enlace

Dos cosas diferentes se confunden constantemente:

  • Dónde escucha la app dentro del contenedor (127.0.0.1 vs 0.0.0.0).
  • Dónde Docker enlaza el puerto publicado en el host (127.0.0.1:PORT vs 0.0.0.0:PORT).

Si cualquiera de los dos es “solo localhost”, los clientes LAN pierden. Y perderás tiempo culpando a la otra capa.

Consejo para Compose: no te enlaces accidentalmente a localhost

Compose admite enlace explícito por IP del host. Esto es genial cuando lo deseas y horrible cuando no.

cr0x@server:~$ cat docker-compose.yml
services:
  web:
    image: nginx:alpine
    ports:
      - "127.0.0.1:8080:80"

Significado: Ese servicio es intencionalmente accesible solo desde el OS host.
Decisión: Si quieres acceso LAN, cámbialo a "8080:80" o "0.0.0.0:8080:80", y luego maneja el alcance del firewall apropiadamente.

Cuando los puertos publicados aún no son alcanzables desde la LAN

Si Docker muestra 0.0.0.0:8080 pero los clientes LAN no pueden conectar:

  • Firewall del host: Firewall de aplicaciones en macOS, Windows Defender Firewall, herramientas de endpoint de terceros.
  • Selección de interfaz: el puerto puede estar enlazado, pero el OS puede bloquear entradas por Wi‑Fi mientras permite por Ethernet (o viceversa).
  • Política de VPN: algunos clientes aplican “bloquear LAN local” para reducir riesgo de movimiento lateral.
  • Quirks de hairpin NAT: algunas redes no te permiten alcanzar tu propia IP pública desde dentro; eso no es Docker, es tu router haciendo lo mejor que puede.

Broma #2: Nada mejora el trabajo en equipo como decirle a alguien “funciona en mi máquina” y querer decirlo como una declaración de arquitectura de red.

DNS: de “es inestable” a “es determinista”

El DNS es donde la extrañeza de Docker Desktop se convierte en folclore. El problema normalmente no es “Docker no puede hacer DNS”.
El problema es: ahora tienes al menos dos resolvedores (OS host y VM), a veces tres (el de la VPN), y no están de acuerdo sobre reglas de horizonte dividido.

Modo de fallo 1: el DNS del contenedor resuelve nombres públicos pero no los internos

Clásico DNS dividido corporativo: git.corp solo se resuelve vía servidores DNS internos, accesibles únicamente con la VPN.
Tu OS host hace lo correcto. Tu contenedor usa un resolvedor stub que no reenvía los dominios correctos a los servidores adecuados.

Opciones de solución, de mejor a peor:

  1. Configurar el DNS de Docker Desktop para usar tus resolvedores internos cuando estés en VPN, y resolvedores públicos cuando no. A veces es un conmutador manual porque “auto” puede ser poco fiable.
  2. DNS por proyecto en Compose:
    • Configura dns: con las IPs de resolvedores que puedan responder tanto nombres internos como externos (a menudo los proporcionados por la VPN).
  3. Codificar /etc/hosts dentro de contenedores. Esto es un parche táctico, no una estrategia.

Task 15: Anular DNS en Compose y verificar dentro del contenedor

cr0x@server:~$ cat docker-compose.yml
services:
  web:
    image: alpine:3.20
    command: ["sleep","infinity"]
    dns:
      - 10.8.0.53
      - 1.1.1.1
cr0x@server:~$ docker compose up -d
[+] Running 1/1
 ✔ Container web-1  Started
cr0x@server:~$ docker exec -it web-1 sh -lc "cat /etc/resolv.conf"
nameserver 10.8.0.53
nameserver 1.1.1.1

Significado: El contenedor ahora usa los servidores DNS que especificaste.
Decisión: Si los dominios internos ahora resuelven, has probado que es un problema de ruta DNS/DNS dividido, no un problema de la aplicación.

Modo de fallo 2: el DNS funciona, pero solo a veces (timeouts, builds lentos, instalaciones de paquetes inestables)

Fallos intermitentes de DNS suelen venir de:

  • Servidores DNS de VPN que sueltan UDP bajo carga o requieren TCP para respuestas grandes.
  • Agentes de seguridad corporativos que interceptan DNS y ocasionalmente tiemblan.
  • Problemas de MTU/MSS en enlaces tunelizados (DNS sobre UDP se fragmenta y luego muere silenciosamente).

Task 16: Detectar timeouts DNS vs NXDOMAIN dentro del contenedor

cr0x@server:~$ docker exec -it web-1 sh -lc "time getent hosts pypi.org >/dev/null; echo $?"
real    0m0.042s
user    0m0.000s
sys     0m0.003s
0

Significado: Éxito rápido.
Decisión: Si esto tarda segundos o falla de forma intermitente, prefiera cambiar resolvedores (o forzar TCP mediante un resolvedor diferente) en vez de reintentar indefinidamente en tus scripts de build.

Modo de fallo 3: el servicio interno funciona por IP pero no por nombre (y solo en VPN)

Eso es DNS dividido otra vez, pero con algo extra: a veces la VPN empuja un sufijo DNS y dominios de búsqueda al OS host,
pero el resolvedor de Docker Desktop no los hereda limpiamente.

Task 17: Confirmar dominios de búsqueda dentro del contenedor

cr0x@server:~$ docker exec -it web-1 sh -lc "cat /etc/resolv.conf"
nameserver 10.8.0.53
search corp.example
options ndots:0

Significado: El dominio de búsqueda está presente.
Decisión: Si falta, los FQDN pueden funcionar mientras que los nombres cortos fallan. Usa FQDNs o configura dominios de búsqueda a nivel de contenedor.

VPNs, túneles divididos y la “ayuda” de los endpoints corporativos

Las VPNs causan dos grandes clases de problemas: cambios de enrutamiento y cambios de DNS. Docker Desktop amplifica ambos porque es efectivamente una red anidada.

Enrutamiento: cuando la VPN se apropia de tu espacio RFC1918

Muchas redes corporativas enrutan grandes rangos privados como 10.0.0.0/8 o 172.16.0.0/12 a través del túnel.
Los valores predeterminados de Docker suelen usar 172.17.0.0/16 para el puente y otros rangos 172.x para redes definidas por el usuario.

En un host Linux puro, normalmente puedes gestionar esto con subredes de puente personalizadas e iptables. En Desktop aún puedes hacerlo, pero debes tratarlo como una configuración de primera clase.

Task 18: Crear una red definida por el usuario en una subred “segura”

cr0x@server:~$ docker network create --subnet 192.168.240.0/24 devnet
9f8c7b6a5d4e3c2b1a0f
cr0x@server:~$ docker run -d --name web2 --network devnet -p 8081:80 nginx:alpine
b1c2d3e4f5a6
cr0x@server:~$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web2
192.168.240.2

Significado: Has movido la red del contenedor lejos de rutas corporativas comunes.
Decisión: Si la alcanzabilidad relacionada con la VPN mejora, institucionaliza una política de subredes para redes de desarrollo.

Seguridad de endpoints: la middlebox invisible

Algunas herramientas de endpoint tratan las NICs virtuales como “no confiables”. Pueden bloquear entradas o salidas, o forzar el tráfico a través de un proxy.
Los síntomas incluyen: puertos publicados funcionan solo cuando se pausa el agente de seguridad, DNS se vuelve lento o servicios internos fallan TLS por inspección.

No puedes “SREar” tu camino fuera de la política. Lo que sí puedes hacer es obtener evidencia rápido y luego escalar con pruebas concretas.

Task 19: Demostrar que es firewall/política local con una prueba rápida de entrada

cr0x@server:~$ python3 -m http.server 18080 --bind 0.0.0.0
Serving HTTP on 0.0.0.0 port 18080 (http://0.0.0.0:18080/) ...

Significado: Esto no es Docker. Es un proceso host simple.
Decisión: Si un par LAN tampoco puede alcanzar esto, deja de depurar Docker y arregla la configuración del firewall/VPN de “bloquear red local”.

Específicos de Windows + WSL2 (a dónde van a morir los paquetes)

En Windows moderno, Docker Desktop a menudo ejecuta su engine dentro de WSL2. WSL2 tiene su propia red virtual (NAT detrás de Windows).
Eso significa que puedes tener: NAT del contenedor detrás de Linux, detrás del NAT de WSL2, detrás de reglas de firewall de Windows. Es NAT hasta el fondo.

Síntomas típicos en Windows

  • Puerto publicado alcanzable desde localhost de Windows pero no desde la LAN. Normalmente reglas de entrada de Windows Defender Firewall, o el enlace es solo loopback.
  • Los contenedores no pueden alcanzar una subred LAN que Windows sí alcanza. Normalmente las rutas de la VPN no se propagan como piensas a WSL2, o la política bloquea interfaces de WSL.
  • El DNS difiere entre Windows y WSL2. WSL2 escribe su propio /etc/resolv.conf; a veces apunta a un resolvedor del lado Windows que no puede ver el DNS de la VPN.

Task 20: Comprobar resolv.conf y tabla de rutas de WSL2 (desde dentro de WSL)

cr0x@server:~$ cat /etc/resolv.conf
nameserver 172.29.96.1
cr0x@server:~$ ip route | head
default via 172.29.96.1 dev eth0
172.29.96.0/20 dev eth0 proto kernel scope link src 172.29.96.100

Significado: WSL2 está usando una puerta de enlace/resolvedor virtual del lado Windows.
Decisión: Si el DNS falla solo con la VPN, considera configurar el comportamiento DNS de WSL2 (resolv.conf estático) y alinear el DNS de Docker con los resolvedores de la VPN.

Específicos de macOS (pf, vmnet y la ilusión de localhost)

En macOS, Docker Desktop ejecuta una VM Linux y reenvía puertos de vuelta a macOS.
Tus contenedores no son ciudadanos de primera clase en tu LAN física. Son invitados detrás de un conserje muy educado.

En qué tropiezan los usuarios de macOS

  • “Funciona en localhost pero no desde mi teléfono.” Normalmente firewall de macOS o puerto publicado solo a loopback.
  • El DNS cambia cuando la Wi‑Fi cambia de red. El resolvedor del host cambia rápidamente; la VM a veces se queda atrás o cachea rarezas.
  • La VPN corporativa bloquea el acceso a la subred local. Tu teléfono no puede alcanzar tu portátil mientras la VPN está conectada, independientemente de Docker.

Task 21: Confirmar que el OS host tiene la IP y la interfaz correctas para pruebas LAN

cr0x@server:~$ ip addr show | sed -n '1,25p'
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 192.168.1.50/24 brd 192.168.1.255 scope global dynamic wlan0

Significado: Tu IP de LAN es 192.168.1.50.
Decisión: Esta es la dirección que un par LAN debe usar para alcanzar tu puerto publicado. Si los pares usan una IP antigua, están probando la máquina equivocada.

Errores comunes: síntoma → causa raíz → solución

1) Síntoma: localhost:8080 funciona, el compañero no puede alcanzar 192.168.x.y:8080

  • Causa raíz: Puerto publicado solo en 127.0.0.1, o firewall del host bloquea entrada.
  • Solución: Publica en todas las interfaces (-p 0.0.0.0:8080:80), luego permite entrada para ese puerto en el firewall del host para el perfil de red correcto.

2) Síntoma: el contenedor puede alcanzar Internet pero no 192.168.1.10 (NAS LAN)

  • Causa raíz: Política de VPN “bloquear LAN local” o rutas que empujan subredes LAN dentro del túnel.
  • Solución: Prueba con la VPN desconectada; si eso lo arregla, solicita excepciones de túnel dividido o ejecuta la carga de trabajo en un entorno adecuado (VM remota, staging). No pelees con la política mediante trucos.

3) Síntoma: el contenedor puede alcanzar IPs LAN pero fallan nombres internos

  • Causa raíz: DNS dividido no propagado a Docker Desktop; contenedores usan un stub que no ve las zonas internas.
  • Solución: Configura DNS por proyecto (dns: en Compose) para incluir servidores DNS corporativos accesibles con la VPN; verifica con getent hosts.

4) Síntoma: DNS falla durante builds (apt/npm/pip falla aleatoriamente)

  • Causa raíz: DNS UDP poco fiable a través de la VPN, problemas de MTU, interceptación de endpoint.
  • Solución: Prefiere resolvedores estables; usa dos resolvedores (interno + público) donde la política lo permita; reduce el riesgo de fragmentación abordando MTU en la capa VPN si la controlas.

5) Síntoma: el servicio está publicado, pero obtienes “connection refused” desde la LAN

  • Causa raíz: La app escucha solo en localhost del contenedor, o se publicó el puerto equivocado.
  • Solución: Comprueba ss -lntp dentro del contenedor; corrige la dirección de enlace; verifica docker port y el mapeo de puertos del contenedor.

6) Síntoma: no puedo conectar a host.docker.internal desde el contenedor

  • Causa raíz: Se anuló el DNS que provee el nombre especial, o estás usando un modo de red donde Desktop no lo inyecta.
  • Solución: Evita anular DNS a ciegas; si debes hacerlo, asegúrate de que el nombre especial siga resolviendo (o añade una entrada explícita vía extra_hosts como último recurso).

7) Síntoma: todo falla solo en una red Wi‑Fi

  • Causa raíz: Esa red aísla clientes (AP isolation) o bloquea conexiones entrantes entre dispositivos.
  • Solución: Usa una red adecuada (o cableada), o ejecuta el servicio detrás de un túnel inverso; no asumas que “misma Wi‑Fi” significa “accesible mutuamente”.

Tres mini-historias corporativas (realistas, anonimizadas, dolorosas)

Mini-historia 1: El incidente causado por una suposición equivocada

Un equipo de producto montó un entorno demo en portátiles para un taller con clientes en sitio. El plan era simple: ejecutar unos servicios en Docker Desktop, publicar puertos
y que los asistentes se conectaran por la Wi‑Fi del hotel. Todos habían hecho “-p 8080:8080” mil veces.
La suposición equivocada fue pensar que Docker Desktop se comporta como un host Linux en una LAN plana.

La mañana del taller, la mitad de los asistentes no pudo conectar. Los servicios estaban arriba. Curl local funcionaba. Los presentadores a veces podían alcanzarse entre ellos.
La gente empezó a reiniciar como si fuera 1998. El problema de red no era Docker; era la Wi‑Fi del hotel haciendo aislamiento de clientes: los dispositivos podían alcanzar Internet pero no entre sí.

La segunda suposición equivocada llegó de inmediato: “Usemos IPs de contenedor y evitamos el mapeo de puertos.”
Intentaron repartir 172.17.x.x visibles dentro de la VM de Docker, que por supuesto no eran alcanzables desde otros portátiles.
Eso llevó a diez minutos de tonterías confiadas y un diagrama de pizarra muy lamentado.

La solución fue aburrida: crear un hotspot local en un teléfono que permitiera tráfico peer‑to‑peer,
y publicar explícitamente los puertos necesarios en 0.0.0.0 con una regla rápida de firewall. Los servicios funcionaron. La suposición sobre “misma red” fue el verdadero fallo.

Mini-historia 2: La optimización que salió mal

Un equipo de plataforma quería builds de CI más rápidos en máquinas de desarrollador. Notaron búsquedas DNS frecuentes durante builds y decidieron “optimizar” forzando a los contenedores a usar un resolvedor DNS público.
En una prueba en cafetería se veía muy bien: resoluciones más rápidas, menos timeouts, bonitas gráficas.

Entonces el primer ingeniero intentó hacer un build mientras estaba en VPN. Los registros de paquetes internos solo eran accesibles vía DNS corporativo y rutas internas.
De repente, los builds fallaron con “host not found” aunque el OS host resolvía bien. La solución temporal fue “desconectar la VPN”,
lo cual es una gran manera de crear el siguiente incidente.

La situación empeoró porque algunos nombres internos resolvían públicamente a IPs placeholder (por razones de seguridad), así que “DNS exitoso” pero las conexiones iban a un agujero negro.
Depurar fue brutal: verías registros A, la app haría timeout y todos culpaban TLS, proxies y Docker en orden aleatorio.

La solución final fue dejar de optimizar DNS globalmente. Pasaron a DNS por proyecto:
resolvedores internos primero cuando están en VPN, resolvedores públicos solo cuando están fuera. También documentaron cómo probar resolución dentro de contenedores, porque “resuelve en mi host” no es un dato válido en una red anidada.

Mini-historia 3: La práctica aburrida pero correcta que salvó el día

Un servicio sensible a la seguridad usaba Docker Desktop para pruebas de integración locales. Necesitaba llamar a una API interna y además aceptar webhooks entrantes de un harness de pruebas en otra máquina de la oficina.
El equipo tenía un hábito que respeto: antes de cambiar ajustes, capturaban evidencia “conocida buena”—rutas, configuración DNS, enlaces de puertos—cuando funcionaba.

Un lunes, todo se rompió tras una actualización del OS. Los contenedores no podían resolver nombres internos. Los webhooks desde una máquina LAN dejaron de llegar.
En vez de adivinar, compararon el estado actual con la línea base: los puertos publicados ahora estaban ligados solo a localhost, y el stub DNS dentro de los contenedores apuntaba a una nueva IP del lado de la VM que no reenviaba DNS dividido.

Arreglaron el enlace de puertos en Compose, luego fijaron el DNS de los contenedores a los resolvedores internos mientras estaban en VPN.
Porque tenían la línea base, pudieron mostrar al equipo de seguridad exactamente qué cambió y por qué.
El incidente no se convirtió en una semana de culpas cruzadas.

Esa práctica—capturar la línea base, comparar cuando se rompe—es tan emocionante como ver pintura secar.
También funciona.

Listas de verificación / plan paso a paso (aburrido a propósito)

Lista 1: Exponer un servicio de Docker Desktop a tu LAN de forma fiable

  1. Asegúrate de que la app escucha en 0.0.0.0 dentro del contenedor (ss -lntp).
  2. Publica el puerto en todas las interfaces: -p 0.0.0.0:8080:80 (o Compose "8080:80").
  3. Confirma que Docker ve el mapeo: docker port CONTAINER.
  4. Confirma que el OS host está escuchando en ese puerto: ss -lntp | grep :8080.
  5. Prueba localmente: curl http://127.0.0.1:8080.
  6. Prueba desde un par LAN: nc -vz HOST_LAN_IP 8080.
  7. Si la prueba LAN falla, ejecuta un listener no Docker (python3 -m http.server) para aislar firewall/VPN de problemas de Docker.

Lista 2: Hacer que los contenedores alcancen recursos LAN internos (NAS, APIs internas)

  1. Desde el OS host, verifica que el objetivo sea alcanzable por IP.
  2. Desde dentro del contenedor, prueba conectividad por IP (nc -vz o curl).
  3. Si la IP falla solo con VPN, comprueba solapamiento de rutas (ip route) y políticas de la VPN (“bloquear LAN local”).
  4. Si la IP funciona pero el nombre falla, comprueba /etc/resolv.conf y resuelve con getent hosts.
  5. Anula DNS por proyecto usando dns: en Compose si es necesario.
  6. Evita solapamiento de subredes: mueve las redes Docker a un rango que tu VPN no enrute.

Lista 3: Estabilizar DNS para builds de desarrollo (pip/npm/apt dejan de fallar)

  1. Mide el tiempo de resolución dentro del contenedor con time getent hosts.
  2. Inspecciona los resolvedores actuales en /etc/resolv.conf.
  3. Si estás en VPN, prefiere los resolvedores internos proporcionados por la VPN (y añade un fallback público solo si está permitido).
  4. No codifiques un DNS público globalmente en todos los proyectos; romperás flujos de trabajo con DNS dividido.
  5. Vuelve a probar dentro del contenedor después de los cambios; no confíes en los resultados del OS host.

Preguntas frecuentes

1) ¿Por qué no puedo usar la IP del contenedor desde otra máquina en mi LAN?

Porque en Docker Desktop esa IP está en un puente interno dentro de una VM Linux (o entorno WSL2). Tu LAN no enruta hacia ella. Publica puertos en su lugar.

2) ¿Por qué -p 8080:80 funciona localmente pero no desde mi teléfono?

Usualmente o el puerto está ligado solo a localhost (explícita o implícitamente via Compose), o tu firewall/VPN del host bloquea conexiones entrantes desde la LAN.

3) ¿Cuál es la diferencia entre 127.0.0.1 y 0.0.0.0 en este contexto?

127.0.0.1 significa “aceptar conexiones solo desde esta misma pila de red”. 0.0.0.0 significa “escuchar en todas las interfaces”.
Necesitas 0.0.0.0 si esperas que otros dispositivos se conecten.

4) ¿Es --network host la solución para la red de Docker Desktop?

No. En Docker Desktop, “host network” no es lo mismo que la red host de Linux y a menudo no te dará lo que quieres. Por defecto usa bridge + puertos publicados.

5) ¿Por qué el DNS funciona en mi host pero no dentro de los contenedores?

El contenedor puede estar usando una ruta de resolvedor diferente (un stub dentro de la VM) y puede que no herede la configuración de DNS dividido de tu VPN. Verifica con cat /etc/resolv.conf y getent hosts dentro del contenedor, luego anula DNS por proyecto si es necesario.

6) ¿Debería configurar el DNS de Docker Desktop a un resolvedor público para “arreglarlo todo”?

Solo si nunca necesitas DNS interno. Los resolvedores públicos pueden romper dominios corporativos, registros de paquetes internos y configuraciones de horizonte dividido. Usa DNS específico por proyecto o comportamiento condicional ligado al estado de la VPN.

7) Mi contenedor no puede alcanzar un dispositivo LAN solo cuando la VPN está conectada. ¿Es culpa de Docker?

Casi nunca. Los clientes VPN pueden enrutar subredes privadas por el túnel o bloquear el acceso a la LAN local. Demuéstralo probando la misma conexión desde el OS host y desconectando la VPN como control.

8) ¿Cuál es la forma más fiable para que un contenedor llame a un servicio en mi portátil?

Usa host.docker.internal y mantenlo consistente entre entornos. Evita IPs de host codificadas que cambian con las redes Wi‑Fi.

9) ¿Cómo sé si el problema es firewall vs mapeo de puertos de Docker?

Ejecuta un listener no Docker en el host (como python3 -m http.server). Si la LAN no puede alcanzar eso, Docker no es el problema.

10) ¿Cuál es un buen principio para la cordura en redes Desktop?

Trata Docker Desktop como “contenedores detrás de una VM detrás de tu OS”. Publica puertos, evita solapamiento de subredes y valida DNS desde dentro del contenedor.

Conclusión: siguientes pasos que puedes hacer hoy

La red de Docker Desktop deja de ser extraña cuando dejas de esperar que sea la red host de Linux. Es una frontera de VM con una capa de reenvío.
Una vez que aceptes eso, la mayoría de los problemas se reducen a tres cubos: direcciones de enlace, firewall/política de VPN y deriva del DNS/resolvedores.

Pasos prácticos:

  1. Escoge un servicio de prueba, publícalo en 0.0.0.0 y verifica la alcanzabilidad LAN de extremo a extremo usando ss, curl y nc.
  2. Captura una línea base cuando las cosas funcionen: docker port, /etc/resolv.conf del contenedor y la tabla de enrutamiento del host.
  3. Si usas VPN, evita que las redes Docker se solapen con rutas corporativas. Estandariza un rango de subred “seguro” para redes de desarrollo.
  4. Haz del DNS una configuración por proyecto cuando los nombres internos importen. Las “soluciones” globales son la forma de crear rupturas entre equipos.

Una idea parafraseada de Werner Vogels (CTO de Amazon): “Todo falla; diseña tus sistemas—y tus operaciones—para absorber esa falla.”
La red de Docker Desktop no es especial. Es simplemente fallo con capas extra.

← Anterior
Ubuntu 24.04: «No se pudo obtener la conexión D-Bus» — arreglar sesiones y servicios rotos (caso #48)
Siguiente →
SLI/CrossFire: Por qué el multi-GPU fue un sueño — y por qué murió

Deja un comentario