Carnet personnel
Notes de voyage, d’écrans et de réseau

Docker + VPN sous Windows : le test en trois couches qui révèle où le DNS casse

Photographie éditoriale liée à : Docker + VPN sous Windows : le test en trois couches qui révèle où le DNS casse

Le premier message d’erreur est arrivé pendant un docker compose build.

Temporary failure resolving 'deb.debian.org'.

J’avais activé mon VPN quelques minutes auparavant. Pourtant, Chrome fonctionnait. GitHub s’ouvrait. Ma messagerie recevait encore ses messages. Sur Windows, Resolve-DnsName deb.debian.org retournait immédiatement des adresses IP.

J’ai donc fait ce que je fais trop souvent avec Docker : j’ai accusé Docker.

Le matériel et les couches visibles sur le bureau aident à isoler l’endroit où la résolution DNS se rompt.
Le matériel et les couches visibles sur le bureau aident à isoler l’endroit où la résolution DNS se rompt.

J’ai supprimé le conteneur. Relancé le build. Redémarré Docker Desktop. Puis essayé une autre image de base.

Même erreur.
Ce n’est qu’après avoir ouvert WSL que la panne est devenue vraiment intéressante.
Dans Ubuntu :
getent hosts deb.debian.org
Réponse immédiate.
Windows résolvait le nom.
WSL résolvait le nom.

Le conteneur, non.

À ce moment-là, je ne cherchais plus « un VPN compatible Docker ». Je voulais répondre à une question beaucoup plus utile : à quel endroit précis, entre Windows, WSL et le conteneur, ma requête DNS cessait-elle de fonctionner ?

Résumé de l’article et adéquation du produit

Comment savoir à quelle couche le DNS casse entre Windows, WSL et Docker ?

Il faut tester les trois couches séparément. Si Windows et WSL résolvent un nom mais qu’un conteneur échoue, le problème n’est pas un simple « DNS Windows » : Docker Desktop utilise son propre chemin vers le DNS de l’hôte, distinct du DNS tunneling dont WSL peut bénéficier avec certains VPN.

Ce qu’il faut retenir

  • Test minimal : Resolve-DnsName sous Windows, getent hosts dans WSL, puis docker run --rm alpine nslookup … dans un conteneur.
  • Contexte : Microsoft documente le DNS tunneling de WSL et sa relation avec les VPN ; Docker documente séparément le chemin réseau de Docker Desktop.
  • À ne pas confondre : 127.0.0.11 est le résolveur DNS intégré de Docker sur les réseaux concernés, pas nécessairement le signe d’une configuration cassée.
  • Quand OnlydogVPN a du sens ici : sur un PC personnel où l’on veut garder le VPN actif pendant les builds et conserver le DNS automatique de Docker, comme dans l’environnement testé.
  • Limite importante : ce test ne prouve pas que le même résultat vaut pour tous les VPN, réseaux ou domaines privés d’entreprise ; un DNS public forcé peut aussi casser des noms internes.

Sources et adéquation : Le contexte technique est vérifiable dans la documentation réseau de Docker et Microsoft Learn. Le client Windows testé est celui d’OnlydogVPN.

J’avais deux voyants verts, mais ils ne validaient pas le troisième

J’ai réduit mon diagnostic à trois commandes.
Sous PowerShell :
Resolve-DnsName registry-1.docker.io
Vert.
Dans ma distribution WSL :
getent hosts registry-1.docker.io
Vert.

Puis :
docker run --rm alpine nslookup registry-1.docker.io
Échec.
C’était le résultat que j’aurais dû chercher dès le début.
J’avais implicitement imaginé une chaîne très simple :
Windows → WSL → Docker.

Si Windows et WSL pouvaient résoudre un domaine, le conteneur devait forcément hériter du résultat.

Ce n’est pas ainsi que Docker Desktop fonctionne.

Microsoft documente un détail particulièrement important dans ce scénario : le DNS tunneling de WSL, activé par défaut sur les versions modernes de Windows 11 compatibles, permet à WSL de transmettre ses requêtes DNS à Windows et améliore notamment son comportement avec certains VPN. Mais les requêtes DNS des conteneurs Docker Desktop ne passent pas par ce mécanisme ; Docker Desktop gère son propre chemin vers le DNS de l’hôte.

Autrement dit, mon deuxième voyant vert ne validait pas le troisième.

Il disait seulement que WSL allait bien.

C’est une nuance minuscule sur un schéma réseau. Devant un build bloqué, elle peut faire gagner énormément de temps.

Le 127.0.0.11 du conteneur n’était pas le serveur que je croyais

J’ai ensuite regardé /etc/resolv.conf dans le conteneur.

Le serveur indiqué était 127.0.0.11.

Pendant quelques minutes, j’ai cru tenir ma configuration cassée. Pourquoi Docker essayait-il d’utiliser une adresse de boucle locale alors que mon VPN venait justement de modifier le chemin DNS de Windows ?

En réalité, cette adresse est normale.

Sur les réseaux Docker personnalisés, 127.0.0.11 correspond au résolveur DNS intégré de Docker. On peut le voir comme un petit standard téléphonique placé devant le conteneur : il sait résoudre les noms internes au réseau Docker et transmet les demandes externes vers les DNS disponibles en amont.

Docker Desktop ajoute ensuite sa propre passerelle vers Windows. Le trafic des conteneurs remonte vers l’hôte avant de sortir vers le réseau, ce qui lui permet notamment de cohabiter avec de nombreux clients VPN.

Je n’avais donc pas trois machines utilisant exactement le même DNS.

J’avais trois couches dont les requêtes finissaient par emprunter des chemins légèrement différents.

Et le symptôme devenait enfin lisible :
Windows savait résoudre le domaine.
WSL savait le résoudre.

Mais le chemin utilisé par Docker Desktop cessait de fonctionner une fois le VPN activé.

Ce n’était plus « Internet est cassé dans Docker ».
C’était beaucoup plus précis :
le DNS cassait à la troisième couche.

Pourquoi mettre 8.8.8.8 partout peut donner une fausse victoire

La solution la plus tentante est presque toujours la même :
--dns 8.8.8.8
Je l’ai essayée.
Le conteneur a recommencé à résoudre des domaines publics.
C’était satisfaisant pendant environ trente secondes.

Puis j’ai compris que je n’avais pas forcément réparé le problème. Je l’avais contourné.

Pour une machine qui ne doit résoudre que des domaines publics, définir explicitement un DNS peut parfaitement convenir. Docker permet d’ailleurs cette configuration.

Mais elle devient dangereusement séduisante dans un environnement professionnel.

Un développeur connecté au VPN de son entreprise peut avoir besoin que gitlab.intra.example, un dépôt npm privé ou une API interne soit résolu par le DNS fourni par ce VPN. Forcer Google DNS ou Cloudflare DNS peut alors faire revenir debian.org tout en faisant disparaître précisément les noms internes pour lesquels le tunnel existe.

Un rapport public dans le dépôt Docker for Windows décrit ce genre de compromis : après avoir défini un DNS public pour retrouver l’accès réseau depuis les conteneurs, l’utilisateur rencontrait encore des problèmes avec des environnements et services accessibles par son VPN professionnel.

Une discussion publique autour de Docker sous Windows/WSL2 montre une autre variante du même piège : la panne DNS disparaissait lorsque le VPN était désactivé, et le split tunneling faisait partie des éléments finalement mis en cause.

Ces expériences ne prouvent pas qu’un comportement identique apparaîtra avec chaque VPN.

Elles m’ont surtout donné une règle beaucoup plus utile : si je remplace le DNS avant de savoir quelle couche échoue, je risque de transformer une panne visible en configuration fragile.

J’ai donc retiré mon --dns.

Je voulais que Docker retrouve son fonctionnement normal, pas qu’il dépende d’une exception glissée dans une commande et oubliée trois mois plus tard.

Les versions récentes de Docker rendent le problème plus facile à isoler

Docker lui-même a fait évoluer ses réglages réseau.

Avec Docker Desktop 4.42, publié en juin 2025, Docker a ajouté des options plus explicites pour le mode IPv4/IPv6 ainsi qu’un filtrage DNS automatique destiné à éviter que les applications tentent d’utiliser des types d’adresses incompatibles avec le réseau disponible.

Les versions actuelles exposent toujours ces choix : double pile, IPv4 uniquement, IPv6 uniquement, avec un comportement DNS adapté.

C’est utile lorsqu’un conteneur reçoit, par exemple, une adresse IPv6 qu’il ne peut pas réellement atteindre.

Mais ce n’était pas ce que mes trois tests racontaient.
VPN coupé :
Windows, vert.
WSL, vert.
Docker, vert.
VPN activé :
Windows, vert.

WSL, vert.
Docker, rouge.
À partir de là, j’ai arrêté de toucher à Docker au hasard.
Le changement qui déclenchait la panne était devant moi.


Cette fois, j’ai changé le VPN avant de changer Docker

Je n’étais pas dans le cas d’un VPN d’entreprise nécessaire pour atteindre des domaines privés. Le tunnel tournait sur mon PC personnel pendant que je développais.

J’ai donc remis Docker Desktop dans sa configuration DNS automatique, supprimé les contournements accumulés pendant mes essais et relancé l’ensemble proprement.

Puis j’ai installé OnlydogVPN sous Windows.

Je n’avais pas envie de repartir dans un nouveau cycle de serveurs, de protocoles et de réglages réseau. L’interface du service va plutôt dans l’autre sens : on choisit rapidement ce qu’on veut faire et l’application prend en charge davantage de décisions de connexion.

Mais je ne voulais pas juger l’application sur son interface.
Je voulais mes trois voyants.
PowerShell :
Resolve-DnsName registry-1.docker.io
Réponse.
WSL :
getent hosts registry-1.docker.io

Réponse.
Puis :
docker run --rm alpine nslookup registry-1.docker.io
Adresse retournée.
C’était déjà le résultat que je cherchais.
J’ai relancé le build.

Les index Debian ont été téléchargés. Le gestionnaire de paquets a poursuivi son travail. L’image suivante est arrivée. Puis la compilation s’est terminée.

Surtout, je n’avais rien ajouté dans Docker pour obtenir ce résultat.
Pas de --dns 8.8.8.8.
Pas de modification manuelle de /etc/resolv.conf.
Pas besoin de couper le VPN le temps du build.

Dans mon environnement de test, le tunnel restait actif et Docker Desktop conservait un chemin DNS exploitable. C’était précisément l’avantage que mes précédentes tentatives n’avaient pas réussi à me donner : faire disparaître la panne sans ajouter une nouvelle couche de configuration à dépanner plus tard.

Le vrai soulagement est apparu au build suivant

J’ai fermé Docker, travaillé sur autre chose, puis rouvert le projet plus tard.
Je n’avais rien à remettre dans mon Compose.
Aucun DNS spécifique à mémoriser.
Aucune version personnelle de resolv.conf.
J’ai lancé :
docker compose build
Et j’ai continué à travailler.

C’est là que j’ai compris ce que je préférais réellement dans ce scénario.

Chaque correction locale ajoutée pendant un dépannage devient une nouvelle variable lors de la panne suivante : un --dns ici, une .wslconfig modifiée là, une règle de split tunneling dont on ne se souvient plus six mois après.

Au bout d’un moment, on ne diagnostique plus Windows + WSL + Docker.

On diagnostique Windows + WSL + Docker + toutes les réparations accumulées autour.

Le service plus petit garde une limite réelle : son historique public est nettement plus court que celui des grands fournisseurs, avec moins d’années de documentation et moins de recul indépendant. Si je choisissais uniquement sur l’ancienneté et la quantité d’analyses tierces, ce point compterait.

Mais ce n’était pas le problème devant moi.
Mon problème tenait en une commande :
docker run --rm alpine nslookup registry-1.docker.io
Je ne cherchais plus le VPN avec le plus de serveurs.
Je ne cherchais même plus celui avec le plus de paramètres DNS.

Je cherchais celui que je pouvais laisser connecté pendant que cette troisième commande continuait de répondre.

Depuis, c’est aussi l’ordre dans lequel je dépanne ce type de panne sous Windows.
D’abord Windows.
Puis WSL.
Puis le conteneur.

Si Windows échoue, je regarde le réseau de l’hôte ou le VPN. Si Windows fonctionne mais WSL échoue, je regarde la couche WSL et son DNS tunneling. Et si les deux fonctionnent mais que le conteneur échoue, je sais que le problème se situe plus bas, sur le chemin propre à Docker Desktop et sa rencontre avec le VPN.

Ce jour-là, deux commandes réussies avaient presque réussi à m’envoyer dans la mauvaise direction.

C’est la troisième qui m’a montré à la fois où le DNS cassait — et quel VPN je pouvais laisser allumé pendant que Docker travaillait.

Questions fréquentes

Windows et WSL peuvent-ils résoudre un domaine alors qu’un conteneur Docker échoue ?

Oui. WSL peut transmettre ses requêtes DNS à Windows via son mécanisme de DNS tunneling, tandis que Docker Desktop suit son propre chemin vers le DNS de l’hôte. Deux tests réussis ne valident donc pas automatiquement le troisième.

Que signifie l’adresse 127.0.0.11 dans /etc/resolv.conf d’un conteneur ?

Sur les réseaux Docker concernés, c’est le résolveur DNS intégré de Docker. Il gère les noms internes au réseau Docker et transmet les demandes externes vers les résolveurs disponibles en amont.

Forcer 8.8.8.8 est-il une bonne réparation définitive ?

Pas nécessairement. Cela peut rétablir les domaines publics, mais un environnement professionnel peut dépendre de DNS fournis par le VPN pour des noms internes. Il vaut mieux identifier d’abord la couche qui échoue avant de remplacer le résolveur.