Problema
En entornos de auto‑hosting es frecuente combinar un contenedor que necesita salir a Internet (por ejemplo, qbittorrent) con una VPN basada en WireGuard. La práctica consiste en montar un archivo wg0.conf dentro del container y delegar la resolución DNS a un resolvedor interno como Unbound. Cuando la configuración parece correcta pero cualquier llamada a curl, ping o la propia aplicación muestra errores de tipo “Could not resolve host”, el contenedor queda sin acceso a la red externa aunque la interfaz wg0 esté UP.
Este patrón aparece en:
- containers que usan la imagen
hotio/qbittorrent(u otras basadas envpn-clientde LinuxServer) - despliegues en TrueNAS, Unraid o servidores domésticos con Docker Compose / Dockge
- configuraciones donde
VPN_KEEP_LOCAL_DNS=falseyVPN_NAMESERVERS=wgdelegan todo el DNS al servidor interno del proveedor VPN
El síntoma principal es la imposibilidad de resolver nombres DNS, mientras que la conectividad de capa 3 (IP) sigue operativa.
Causa
Los fallos de DNS en este tipo de stack suelen deberse a una combinación de factores:
-
Unbound no escucha en la dirección esperada
La variableVPN_NAMESERVERS=wgindica al script de inicio que añada el servidor DNS que viene en el bloque[Interface] DNS = …delwg0.conf. Si el procesounboundse inicia pero no tiene permiso para abrir el puerto 53 en la namespace de red del contenedor, las consultas quedan en timeout. -
/etc/resolv.confno se actualiza
Algunos scripts sólo sobrescribenresolv.confcuandoVPN_KEEP_LOCAL_DNS=true. Con la opción en false el archivo sigue apuntando a los servidores del host (por ejemplo, 1.1.1.1) que no son alcanzables a través dewg0, provocando la falla. -
Rutas predeterminadas mal definidas
WireGuard añade una ruta0.0.0.0/0 dev wg0. Si la política de enrutamiento del contenedor mantiene una ruta por defecto aeth0y la regla desrc_valid_markno está aplicada, el tráfico DNS se envía por la interfaz equivocada y nunca llega al servidor VPN. -
MTU incorrecto
El bloqueMTU = 1320es típico para conexiones PPPoE, pero si la red subyacente tiene un MTU mayor y no se ajusta en el contenedor, los paquetes DNS fragmentados pueden ser descartados sin notificación. -
AllowedIPsdemasiado amplio sin excepción para la red LAN
CuandoAllowedIPs = 0.0.0.0/0se usa sinVPN_LAN_NETWORKo sin rutas estáticas a la subred local, el contenedor intenta resolver DNS a través de la VPN, pero el servidor DNS del proveedor solo responde a peticiones provenientes de la IP del túnel (10.x.x.x). Cualquier petición desde la IP del host (172.x.x.x) es descartada.
Solución
1. Forzar la generación correcta de resolv.conf
Establecer VPN_KEEP_LOCAL_DNS=true obliga al entrypoint a reemplazar el archivo con el DNS del túnel. Si se prefiere mantener los DNS locales, añadir manualmente la línea al Dockerfile o al docker-compose.yml:
environment:
- VPN_KEEP_LOCAL_DNS=true
2. Verificar que Unbound escucha en 0.0.0.0:53
Dentro del container, ejecutar:
netstat -lnp | grep ':53'
Si no aparece, iniciar Unbound en modo forward con una configuración mínima:
cat > /config/unbound.conf <<'EOF'
server:
interface: 0.0.0.0
access-control: 0.0.0.0/0 allow
forward-zone:
name: "."
forward-addr: 10.128.0.1
EOF
Reiniciar el servicio (s6-svc -r /var/run/svc.d/unbound o recrear el container).
3. Añadir regla de enrutamiento para tráfico DNS
Crear una regla de marca que obligue al tráfico de origen 10.x.x.x a usar wg0:
ip rule add from 10.0.0.0/8 table 200
ip route add default dev wg0 table 200
En Docker Compose se puede inyectar mediante sysctls o cap_add: - NET_ADMIN.
4. Ajustar MTU si persisten timeouts
Probar con un MTU ligeramente mayor (por ejemplo, 1380) y validar con ping -M do -s 1400 1.1.1.1. Si los paquetes llegan, actualizar el bloque [Interface]:
MTU = 1380
5. Definir explícitamente la red LAN en la variable VPN_LAN_NETWORK
Esto evita que el tráfico a la subred local (por ejemplo, 192.168.0.0/24) sea forzado a la VPN:
environment:
- VPN_LAN_NETWORK=192.168.0.0/24
Con esta excepción, el contenedor puede seguir resolviendo nombres internos del host si fuera necesario.
6. Reemplazar el script de inicio por una versión que respete VPN_NAMESERVERS=wg
Algunas imágenes usan wireguard-go y un wrapper que solo escribe nameserver 127.0.0.1 cuando UNBOUND_ENABLED=true. Si se desactiva Unbound, el DNS queda sin backend. La solución práctica es:
- Dejar
UNBOUND_ENABLED=truey usar la configuración mínima mostrada en el punto 2, o - Cambiar
VPN_NAMESERVERSa una lista de servidores públicos (1.1.1.1,8.8.8.8) y desactivar la inserción automática del DNS del túnel.
Cuándo aplicar esta solución
Utilice este conjunto de pasos cuando:
- El contenedor muestra “Could not resolve host” pese a que
wg0estáUP. - Los logs del contenedor indican
[VPN] [IPV4] IP lookup failed!. - La variable
UNBOUND_ENABLEDestá en false peroVPN_NAMESERVERS=wgestá presente. - Se necesita que todo el tráfico (incluido DNS) salga por la VPN sin perder acceso a la LAN.
No es necesario aplicar todo el checklist si:
- El contenedor ya resuelve DNS correctamente usando servidores externos.
- La VPN se configura en modo split‑tunnel y el DNS local no está dentro del túnel.
- Se usa una imagen que gestiona DNS de forma diferente (por ejemplo,
gluetun).
Código
# docker-compose.yml (fragmento esencial)
services:
qbittorrent:
image: ghcr.io/hotio/qbittorrent
container_name: qbittorrent
restart: unless-stopped
ports:
- "8080:8080"
environment:
- PUID=568
- PGID=568
- TZ=America/New_York
- VPN_ENABLED=true
- VPN_CONF=wg0
- VPN_PROVIDER=generic
- VPN_LAN_NETWORK=192.168.0.0/24
- VPN_KEEP_LOCAL_DNS=true
- VPN_NAMESERVERS=wg
- UNBOUND_ENABLED=true # activar Unbound
cap_add:
- NET_ADMIN
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
volumes:
- ./configs/:/config
- /mnt/tank/library:/media
- /mnt/tank/downloads/hotio-qbit:/downloads
# /config/unbound.conf (mínimo)
server:
interface: 0.0.0.0
access-control: 0.0.0.0/0 allow
forward-zone:
name: "."
forward-addr: 10.128.0.1
Verificación
-
Comprobar resolvers
cat /etc/resolv.conf # debe contener 10.128.0.1 o 127.0.0.1 si Unbound está activo -
Test de DNS interno
dig @127.0.0.1 ip.me +short # debe devolver una IP pública -
Ping externo
ping -c 3 1.1.1.1 -
Comprobar ruta predeterminada
ip route show default # la tabla debe indicar dev wg0 -
Revisar logs
docker logs qbittorrent 2>&1 | grep -i 'unbound\|vpn' # ausencia de “IP lookup failed” confirma la solución
Notas adicionales
- En TrueNAS SCALE, la red del host a veces impone su propio
dnsmasq. Asegúrese de que el contenedor no herede/etc/resolv.confdel host montándolo comotmpfso usandoread_only: trueen la sección de volúmenes. - Si el proveedor VPN usa DNS sobre TLS, Unbound necesita la opción
forward-tls-upstream: yesy los certificados del servidor. - Cuando se usa
docker compose up --detach, los cambios enunbound.confrequieren recrear el container (docker compose up -d --force-recreate). - Mantenga el archivo
wg0.confactualizado; cualquier cambio en la IP del servidor DNS del proveedor obliga a reiniciar el contenedor para que el script vuelva a escribirresolv.conf.
Con estos ajustes, la mayoría de los despliegues que combinan Docker, WireGuard y Unbound recuperan la capacidad de resolución DNS y vuelven a descargar torrents o a ejecutar cualquier cliente que dependa de nombres de dominio.