Vous montez votre projet dans un conteneur avec un bind mount. Ça marche. Puis ça ne marche plus. Soudain votre build ne peut plus écrire un cache, votre application ne peut pas créer de fichier de log, et tout est soit possédé par root soit « Accès refusé ». Bienvenue dans la vallée étrange des permissions sur Windows : ça ressemble à Unix, ça sent Unix, mais ça se comporte comme un comité.
Si vous voulez l’option la moins contraignante, vous devez choisir une stratégie : soit traiter Windows comme un hôte que vous empruntez (et faire le travail à l’intérieur de WSL2), soit accepter la couche de traduction et mettre en place des garde-fous. La bonne nouvelle : vous pouvez rendre ça ennuyeux. La mauvaise : il faut être précis.
La règle unique qui simplifie tout
Gardez vos fichiers de projet à l’intérieur du système de fichiers Linux de WSL2 (par ex. /home/you/project), pas sur C:\, et faites vos bind mounts depuis là.
C’est tout. Si vous faites cela, les permissions se comportent comme sous Linux parce que… c’est Linux. Dès que vous montez un chemin Windows dans un conteneur Linux, vous demandez à deux modèles de permissions, deux sémantiques de système de fichiers et une frontière de virtualisation de « simplement s’accorder ». Ils ne le feront pas. Ils vont négocier. Et la négociation, c’est ce qui provoque des incidents à 3 h du matin.
Une petite blague pour la route : la traduction des permissions Windows → Linux, c’est comme un tout-petit bilingue qui traduit des contrats légaux — effort impressionnant, précision douteuse.
Si votre équipe insiste pour travailler depuis C:\Users\..., vous pouvez quand même rendre ça fonctionnel. Ce ne sera juste pas la solution la moins pénible. Ce sera de la « douleur avec un objectif de niveau de service ».
Comment les permissions deviennent étranges sur Windows (ce qui se passe réellement)
Les bind mounts ne sont pas juste des « fichiers partagés dans des conteneurs »
Un bind mount, c’est le conteneur qui voit un répertoire physiquement stocké ailleurs. Sur des hôtes Linux, c’est simple : la VFS du noyau a des sémantiques communes ; UID/GID et les bits de mode signifient ce que les conteneurs pensent qu’ils signifient.
Sur Windows, Docker Desktop fait essentiellement tourner une VM Linux (moteur WSL2) puis présente les fichiers Windows à cette VM via une couche de système de fichiers partagé. Votre conteneur ne monte pas NTFS directement ; il monte une traduction de NTFS.
Où le décalage apparaît
- UID/GID vs SIDs/ACLs : Linux utilise des identifiants numériques utilisateur/groupe et des bits de mode. Windows utilise des SIDs et des ACLs. Le mappage est avec perte.
- Sémantiques de chmod/chown : Sur un système de fichiers Linux « réel »,
chmodetchownfont généralement ce qu’ils disent. Sur des mounts fournis par Windows, ils peuvent sembler réussir sans persister, ou échouer avec « Operation not permitted ». - Bit exécutable : Linux a besoin du bit exécutable pour scripts/binaire. Windows non. La couche de traduction fait des suppositions.
- Sensibilité à la casse : Linux est sensible à la casse, Windows généralement pas (même si Windows moderne peut activer la sensibilité à la casse par répertoire). Les outils qui partent d’un modèle peuvent se comporter étrangement dans l’autre.
- Inotify / surveillance de fichiers : Les serveurs de dev et les systèmes de build dépendent des événements filesystem. Les événements traversant les frontières peuvent être retardés ou perdus. Ça ressemble à « le hot reload est cassé ».
Deux « configurations Windows » différentes que l’on confond
Configuration A : travailler dans le système de fichiers WSL2 (meilleur). Votre code vit sous /home dans WSL. Le moteur Docker est basé sur WSL2. Les conteneurs montent des chemins Linux. Vous arrêtez en grande partie de penser aux permissions Windows.
Configuration B : travailler sur le système de fichiers Windows (parfois nécessaire). Votre code est sous C:\. Docker monte /mnt/c/... dans les conteneurs. Vous êtes alors en territoire de traduction. Il vous faut des politiques : quel utilisateur s’exécute dans le conteneur, quels répertoires sont modifiables, et comment éviter les artefacts possédés par « root ».
Il y a un aphorisme fiabilité utile à garder sur votre bureau. Voici une idée paraphrasée, car la formulation exacte varie : idée paraphrasée : « Les systèmes complexes échouent de façons complexes ; la simplicité est une caractéristique de fiabilité. »
— John Ousterhout (idée paraphrasée)
Architecture de référence la moins contraignante (que faire)
Décision 1 : Placez l’espace de travail dans WSL2
Faites de WSL2 votre « système de fichiers poste de développement », même si Windows est votre bureau. Vous pouvez toujours utiliser VS Code ou JetBrains sur Windows et éditer les fichiers dans WSL via leur intégration WSL. Vos outils Git, les toolchains de langage et les permissions de fichiers s’aligneront.
Décision 2 : Exécutez les conteneurs en tant qu’utilisateur non-root correspondant à votre UID/GID WSL
Si votre conteneur écrit des fichiers dans un bind mount, vous voulez que ces fichiers appartiennent à votre utilisateur WSL, pas à root. Le pattern pragmatique :
- Déterminez votre UID/GID WSL (
id). - Construisez une image qui crée un utilisateur avec cet UID/GID (ou utilisez une logique d’entrypoint).
- Dans Compose, définissez
user: "UID:GID"pour les conteneurs de dev.
Décision 3 : Utilisez des volumes nommés pour les répertoires à forte écriture
Les bind mounts sont parfaits pour le code source. Ils peuvent être terribles pour les caches de dépendances et les bases de données. Placez-les dans des volumes Docker :
- Node :
node_modulessouvent plus rapide dans un volume. - Python :
.venv, cache pip. - Rust/Go/Java : caches de build et répertoires d’artefacts.
- Bases de données : utilisez toujours des volumes sauf si vous aimez les histoires de corruption.
Décision 4 : Traitez les montages Windows comme majoritairement en lecture si vous devez les utiliser
Si la politique corporate ou des outils vous forcent à garder le code sur C:\, traitez-le comme un lecteur réseau partagé : lisez-le, compilez depuis là si nécessaire, mais n’écrivez pas des milliers de petits fichiers à travers lui. Redirigez les caches vers des volumes ou un stockage local au conteneur.
Deuxième petite blague : la façon la plus rapide d’améliorer les performances des bind mounts sur Windows est d’arrêter de faire des bind mounts Windows. Ce n’est pas élégant, mais ça marche.
Plan d’installation (étape par étape)
1) Confirmer que vous utilisez le moteur WSL2
Docker Desktop peut fonctionner avec différents backends selon la version et la configuration. Si vous voulez la voie la moins contraignante, vous voulez WSL2.
2) Créer un dossier de projet dans WSL2
Clonez votre dépôt à l’intérieur de WSL, pas sous /mnt/c. Si vous avez déjà un clone Windows, re-clonez. Oui, cela semble redondant. Non, ce n’est pas une perte de temps.
3) Ajouter un utilisateur de dev à votre image (ou exécuter avec votre UID)
Pour les images de développement, je préfère intégrer l’utilisateur dans l’image parce que ça rend le débogage prévisible. Pour du rapide et sale, user: "${UID}:${GID}" dans Compose suffit.
4) Séparer les « bind mounts de source » des « volumes à forte écriture »
Montez votre source de projet. Mettez les caches et l’état dans des volumes. Cela réduit les surprises de permissions et rend les performances moins dépendantes du pont Windows‑WSL.
5) Décidez d’une politique pour la propriété des fichiers
Choisissez-en une et appliquez-la :
- Politique A (recommandée) : le conteneur écrit avec le même UID/GID que votre utilisateur WSL.
- Politique B : le conteneur écrit en root, et vous acceptez que les modifications/nettoyages côté hôte nécessitent sudo. C’est courant, et c’est une source lente de frustration.
- Politique C : le conteneur n’écrit jamais sur les bind mounts ; il écrit uniquement dans des volumes. Idéal pour des builds hermétiques, moins pratique pour certains cycles de dev.
Tâches pratiques : commandes, sorties et décisions (12+)
Voici les tâches que je lance réellement quand quelqu’un dit « les bind mounts sont cassés ». Chacune inclut : la commande, ce que la sortie signifie, et la décision suivante.
Task 1: Check Docker is reachable and which context you’re on
cr0x@server:~$ docker context ls
NAME DESCRIPTION DOCKER ENDPOINT ERROR
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock
desktop-linux Docker Desktop unix:///var/run/docker.sock
Signification : Vous voyez quel endpoint Docker vous atteignez. Sur Windows avec WSL2, vous utilisez typiquement le context Desktop Linux.
Décision : Si vous n’êtes pas sur le context attendu, changez : docker context use desktop-linux. Les problèmes de « Permission denied » s’avèrent parfois être « vous parlez à un moteur différent de celui que vous pensez ».
Task 2: Confirm WSL distro and kernel (WSL2 vs WSL1)
cr0x@server:~$ uname -a
Linux cr0x 5.15.133.1-microsoft-standard-WSL2 #1 SMP Wed Oct 25 00:00:00 UTC 2023 x86_64 GNU/Linux
Signification : Le noyau WSL2 est en jeu (notez « WSL2 »).
Décision : Si vous n’êtes pas en WSL2, réglez cela d’abord. WSL1 a un comportement de système de fichiers différent et a tendance à aggraver les scénarios Docker Desktop.
Task 3: Find where your project actually lives
cr0x@server:~$ pwd
/mnt/c/Users/cr0x/work/myapp
Signification : Vous êtes sur le système de fichiers monté depuis Windows. Attendez-vous à des bizarreries de permissions et éventuellement à de mauvaises performances.
Décision : Déplacez ou re-clonez dans /home/cr0x/work/myapp. Si la politique l’empêche, continuez avec une configuration défensive (tâches plus loin).
Task 4: Identify your UID/GID in WSL
cr0x@server:~$ id
uid=1000(cr0x) gid=1000(cr0x) groups=1000(cr0x),27(sudo),44(video)
Signification : Votre utilisateur est UID 1000, GID 1000.
Décision : Utilisez 1000:1000 comme utilisateur du conteneur pour les bind mounts de dev, ou intégrez cela dans l’image.
Task 5: Reproduce the permission problem in a minimal container
cr0x@server:~$ docker run --rm -it -v "$PWD:/work" alpine:3.20 sh -lc 'id; ls -ld /work; touch /work/.permtest && ls -l /work/.permtest'
uid=0(root) gid=0(root) groups=0(root)
drwxrwxrwx 1 root root 4096 Jan 3 10:31 /work
-rwxrwxrwx 1 root root 0 Jan 3 10:31 /work/.permtest
Signification : Le conteneur s’exécute en root ; le fichier créé appartient à root. Les bits de mode sont larges (777), ce qui est courant sur des mounts fournis par Windows.
Décision : Si les artefacts root sont le problème, exécutez le conteneur avec votre UID/GID.
Task 6: Test running container as your UID/GID
cr0x@server:~$ docker run --rm -it --user 1000:1000 -v "$PWD:/work" alpine:3.20 sh -lc 'id; touch /work/.uidtest && ls -l /work/.uidtest'
uid=1000 gid=1000
-rwxrwxrwx 1 1000 1000 0 Jan 3 10:32 /work/.uidtest
Signification : La propriété du fichier correspond maintenant à l’UID/GID 1000 à l’intérieur du conteneur. Sur des systèmes de fichiers Linux natifs, cela se mappe proprement. Sur des mounts Windows, vous pouvez encore avoir des bits de mode permissifs et un comportement étrange, mais au moins vous évitez les fichiers root.
Décision : Intégrez user: "1000:1000" dans Compose pour le dev, ou créez l’utilisateur dans le Dockerfile.
Task 7: Check whether chmod/chown actually works on your mount
cr0x@server:~$ docker run --rm -it -v "$PWD:/work" alpine:3.20 sh -lc 'touch /work/chmodtest; chmod 600 /work/chmodtest; ls -l /work/chmodtest'
-rwxrwxrwx 1 root root 0 Jan 3 10:33 /work/chmodtest
Signification : chmod n’a pas pris (toujours 777-ish). C’est typique sur des mounts Windows sans support de métadonnées.
Décision : Ne basez pas la sécurité sur les bits de mode sur ce mount. Utilisez des contrôles au niveau application, ou déplacez l’espace de travail sur le système de fichiers WSL où chmod fonctionne.
Task 8: Verify if you are on Windows mount (DrvFs) vs native Linux filesystem
cr0x@server:~$ df -T .
Filesystem Type 1K-blocks Used Available Use% Mounted on
C:\ 9p 976762876 123456789 853306087 13% /mnt/c
Signification : Le type de système de fichiers est 9p (commun pour les montages Windows dans WSL2). C’est la frontière de traduction.
Décision : Attendez-vous à des bizarreries. Si vous avez besoin de permissions Unix correctes, déplacez-vous vers ext4 à l’intérieur de WSL2 (/home), pas /mnt/c.
Task 9: Inspect mount options from inside the container
cr0x@server:~$ docker run --rm -v "$PWD:/work" alpine:3.20 sh -lc 'mount | grep " /work " || true'
/dev/sdd on /work type 9p (rw,relatime,dirsync,aname=drvfs;path=C:\Users\cr0x\work\myapp;uid=0;gid=0;metadata;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=8,wfd=8)
Signification : Vous voyez littéralement que vous êtes sur un montage de type DrvFs/9p, avec des flags comme metadata qui influencent le comportement des permissions.
Décision : Si metadata est absent, chmod/chown ont encore moins de sens. Si metadata est présent, le comportement peut être meilleur, mais toujours pas identique à ext4.
Task 10: Compose sanity check: what user is the service running as?
cr0x@server:~$ docker compose exec app sh -lc 'id; umask'
uid=0(root) gid=0(root) groups=0(root)
0022
Signification : Votre service Compose s’exécute en root. Le umask est normal, mais sans importance si les bits de mode ne persistent pas.
Décision : Définissez user: dans Compose ou corrigez l’image. En dev, root crée rarement moins de dégâts collatéraux.
Task 11: Measure mount performance quickly (tiny-file workload)
cr0x@server:~$ docker run --rm -v "$PWD:/work" alpine:3.20 sh -lc 'time sh -c "i=0; while [ $i -lt 2000 ]; do echo $i > /work/t_$i; i=$((i+1)); done"'
real 0m6.842s
user 0m0.187s
sys 0m1.214s
Signification : Créer 2000 petits fichiers a pris plusieurs secondes. Sur un système de fichiers Linux natif, c’est souvent bien plus rapide.
Décision : Si votre outil de build écrit des milliers de fichiers (bonjour Node et Java), déplacez les caches dans des volumes ou mettez tout le projet dans le système de fichiers WSL.
Task 12: Compare with a Docker named volume (control experiment)
cr0x@server:~$ docker run --rm -v perfvol:/work alpine:3.20 sh -lc 'time sh -c "i=0; while [ $i -lt 2000 ]; do echo $i > /work/t_$i; i=$((i+1)); done"'
real 0m0.403s
user 0m0.179s
sys 0m0.205s
Signification : Le volume nommé est nettement plus rapide pour des charges à forte écriture. De plus : les permissions sont entièrement selon la sémantique Linux à l’intérieur du volume.
Décision : Placez les caches de build et les répertoires de dépendances dans des volumes. Gardez les bind mounts pour le code source et les configs.
Task 13: Confirm file ownership on the host side (WSL) after container writes
cr0x@server:~$ ls -ln .uidtest .permtest 2>/dev/null
-rwxrwxrwx 1 1000 1000 0 Jan 3 10:32 .uidtest
-rwxrwxrwx 1 0 0 0 Jan 3 10:31 .permtest
Signification : Des fichiers appartenant à root existent parce que des écritures précédentes ont été faites en root ; le fichier mappé UID est possédé par 1000.
Décision : Si des artefacts root existent, corrigez l’utilisateur du conteneur et nettoyez le répertoire (peut nécessiter sudo dans WSL).
Task 14: Inspect a volume and confirm it’s not accidentally bind-mounted
cr0x@server:~$ docker volume inspect perfvol
[
{
"CreatedAt": "2026-01-03T10:34:12Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/perfvol/_data",
"Name": "perfvol",
"Options": null,
"Scope": "local"
}
]
Signification : C’est un volume Docker local à l’intérieur de la VM Linux. Bien. C’est pourquoi il est rapide et correct pour les permissions.
Décision : Utilisez des volumes pour les chemins fortement sollicités. Si quelqu’un insiste « le bind mount suffit », montrez-lui les chiffres des tâches 11 vs 12.
Méthode de diagnostic rapide
Quand quelque chose est lent ou que vous avez un « permission denied », vous n’avez pas besoin d’une semaine de théorie. Vous avez besoin de trois contrôles qui isolent le domaine.
Premier : Montez-vous des fichiers Windows ou Linux ?
- Exécutez
pwdetdf -T .dans WSL. - Si vous êtes sous
/mnt/cet que le type de FS ressemble à9p, vous êtes en territoire de traduction. - Décision : Si le problème est la correction des permissions ou les performances, déplacez l’espace de travail dans
/home(ext4) comme solution par défaut.
Second : Quel utilisateur écrit dans le bind mount ?
- Exécutez
docker compose exec app id(oudocker run ... id). - Si c’est root, vous aurez des artefacts root et parfois des échecs d’écriture selon la couche de montage.
- Décision : Forcez UID/GID via
user:dans Compose ou créez un utilisateur correspondant dans l’image.
Troisième : S’agit-il d’un problème de correction ou de performance ?
- Exécutez le benchmark petit-fichiers (Tâche 11) sur le bind mount et comparez à un volume (Tâche 12).
- Décision : Si le volume est beaucoup plus rapide, ne « tunnez » pas les permissions. Changez l’agencement du stockage : bind mount pour la source, volume pour la zone de churn.
Vérification bonus : chmod/chown est-il censé fonctionner ici ?
- Essayez le test de chmod (Tâche 7). Si ça ne tient pas, ne concevez pas des workflows autour des bits POSIX pour ce mount.
- Décision : Déplacez-vous vers le système de fichiers WSL ou gardez les fichiers sensibles hors des mounts Windows.
Erreurs courantes : symptômes → cause racine → correction
1) « Permission denied » lors de l’écriture sur un bind mount
Symptôme : L’application échoue à créer des fichiers sous /work ou /app avec EACCES.
Cause racine : Le conteneur s’exécute en tant qu’utilisateur non-root, mais le répertoire bind‑mounté correspond à une ACL Windows qui n’autorise pas l’écriture comme attendu ; ou le répertoire est possédé par root parce que des exécutions précédentes l’ont créé en root.
Correction : Déplacez le projet dans le fichier système WSL. Sinon, exécutez le conteneur avec votre UID/GID WSL et nettoyez les fichiers root (dans WSL) avec sudo.
2) « chmod: Operation not permitted » ou chmod ne change rien
Symptôme : chmod renvoie une erreur, ou ls -l ne change jamais.
Cause racine : Les mounts fournis par Windows ne supportent pas pleinement la sémantique des métadonnées POSIX (ou le support metadata n’est pas activé).
Correction : Ne comptez pas sur chmod sur les mounts Windows. Stockez scripts/binaire dans le système de fichiers WSL. Si vous avez besoin de fidélité aux permissions Unix, utilisez ext4 dans WSL2 ou un volume nommé.
3) Le hot reload / watcher de fichiers ne détecte pas les changements
Symptôme : Node/webpack, rechargers Python, ou live reload Go ne se déclenchent pas au save.
Cause racine : La traduction des événements fichiers entre Windows ↔ WSL2 ↔ conteneur est incohérente, surtout sur /mnt/c.
Correction : Gardez l’espace de travail dans WSL. Si vous êtes coincé sur des mounts Windows, configurez des watchers en polling (plus lent mais fiable) ou déplacez uniquement les répertoires surveillés dans WSL.
4) Le build est extrêmement lent, le CPU semble inactif
Symptôme : Installer des dépendances ou compiler prend une éternité ; l’utilisation CPU est faible.
Cause racine : Les I/O de petits fichiers sont bloquées par la couche de système de fichiers partagé ; chaque stat/open/write traverse des frontières.
Correction : Mettez les chemins à forte écriture dans des volumes ; déplacez le projet dans le système de fichiers WSL ; évitez de monter les répertoires de dépendances depuis Windows.
5) « Tout est possédé par root » et Git commence à râler
Symptôme : Vous voyez une propriété root, et Git se plaint de propriété douteuse ou refuse d’opérer dans certaines configurations.
Cause racine : Le conteneur a tourné en root et a écrit dans le bind mount ; la propriété a persisté. Sur des environnements mixtes, les vérifications de sécurité se déclenchent.
Correction : Arrêtez d’exécuter en root pour le dev. Nettoyez l’espace de travail, et rendez la propriété déterministe via le mappage UID/GID.
6) Le conteneur de base de données fonctionne jusqu’à ce qu’il ne fonctionne plus
Symptôme : Postgres/MySQL démarre, puis crashe avec des soucis fsync ou des erreurs de permission ; ou les performances s’effondrent.
Cause racine : La base de données est bind‑mountée sur le système de fichiers Windows ; les garanties POSIX et la sémantique fsync ne sont pas les mêmes.
Correction : Utilisez toujours un volume nommé pour les répertoires de données des bases, surtout sur des hôtes Windows.
7) « Mais ça marche sur ma machine » dans une équipe à forte présence Windows
Symptôme : L’environnement d’un dev est OK ; un autre a des erreurs de permission et des lenteurs.
Cause racine : Les espaces de travail sont à des emplacements différents (WSL vs /mnt/c), différents réglages Docker Desktop, et des utilisateurs par défaut différents dans les images.
Correction : Standardisez : emplacement de l’espace de travail, utilisateur Compose, et stratégie de volumes. Traitez « environnement de dev » comme une infra avec une spécification.
Trois mini-histoires d’entreprise (comment ça casse en vrai)
Mini-histoire 1 : L’incident causé par une mauvaise hypothèse
Une équipe avait un setup dev cross‑platform : laptops Linux, macOS, et une grosse équipe Windows. Ils ont déployé un nouvel environnement de dev containerisé avec un bind mount du repo et un conteneur s’exécutant en root « pour éviter des problèmes de permissions ». Personne n’aimait ça, mais ça semblait suffire — jusqu’à ce que ça ne suffise plus.
L’hypothèse erronée était subtile : ils supposaient que « root dans le conteneur peut toujours écrire dans le bind mount ». Sur hôtes Linux, c’est souvent vrai (hors SELinux/AppArmor). Sur des mounts Windows, root ne contourne pas magiquement les décisions ACL Windows. La couche de traduction peut bloquer des opérations, et parfois le fait de manière incohérente selon la configuration du partage sur chaque machine.
Cela a émergé sous forme d’échecs intermittents d’un générateur de code qui écrivait dans le repo. La moitié des développeurs Windows voyaient « permission denied », l’autre moitié non, et le générateur gérait mal les erreurs. Il réessayait à outrance et laissait des sorties partielles. Le repo s’est retrouvé avec des fichiers générés aux propriétés différentes et timestamps incohérents. Les builds échouaient, puis réussissaient, puis échouaient à nouveau.
Ils ont traité cela comme un outil capricieux. Ce n’était pas le cas. C’était la sémantique du stockage. La correction était ennuyeuse : déplacer l’espace de travail dans WSL2, exécuter les conteneurs en tant qu’utilisateur WSL, et stocker les artefacts générés dans un volume avec une étape d’export contrôlée. Le générateur a cessé d’« échouer aléatoirement », parce qu’il ne négociait plus avec NTFS via un tuyau étroit.
Mini-histoire 2 : L’optimisation qui s’est retournée contre eux
Un groupe voulait accélérer les installs pour un monorepo JavaScript. Quelqu’un a proposé de bind-monter node_modules depuis l’hôte pour que les dépendances persistent entre rebuilds. C’était vendu comme « un cache gratuit ». La direction aimait le gratuit.
Sur Linux, c’était acceptable. Sur Windows, c’était un accident en slow motion. L’installation des dépendances impliquait la création et suppression d’un grand nombre de petits fichiers, et des symlinks. La couche de montage Windows gérait ça mais avec un fort overhead. Les installs devenaient plus lentes qu’une installation propre dans le système de fichiers du conteneur, et les watchers de fichiers devenaient peu fiables.
Pire : les permissions partaient en vrille. Certains devs faisaient tourner des conteneurs en root, d’autres en utilisateur. Le même répertoire de dépendances a fini avec une propriété mixte. Les outils ont commencé à échouer sur EPERM, et les devs « corrigeaient » en supprimant le répertoire et réinstallant — en boucle.
Ils ont finalement annulé l’« optimisation » et remplacé cela par un volume nommé pour les caches de dépendances, et un volume séparé pour node_modules quand nécessaire. Le repo est resté bind‑mounté, mais le churn a été relégué aux volumes. L’équipe a cessé de payer une taxe I/O à la couche de montage Windows. La leçon : la mise en cache n’est pas gratuite si elle est faite au mauvais endroit.
Mini-histoire 3 : La pratique ennuyeuse qui a sauvé la mise
Une équipe d’entreprise régulée avait une pratique étonnamment saine : tout changement d’environnement développeur nécessitait une petite mise à jour « ops-style » de leur checklist. Pas une novella bureaucratique — juste une page avec des commandes standard pour vérifier correction, performance et propriété.
Quand ils ont adopté Docker Desktop avec WSL2, ils ont ajouté trois vérifications : (1) le chemin du projet est sous /home, (2) docker compose exec app id correspond à l’UID du développeur, et (3) le benchmark petit-fichiers sur un bind mount ne dépasse pas un seuil approximatif. Si c’était le cas, la checklist leur demandait de déplacer les caches dans des volumes.
Des mois plus tard, une mise à jour Docker Desktop a changé le comportement pour un sous‑ensemble de machines. Les gens se sont mis à se plaindre de builds lents et d’un chmod étrange. La checklist a rendu la cause évidente : l’espace de travail avait été déplacé vers /mnt/c parce qu’un nouvel arrivant avait suivi un vieux guide d’onboarding qui supposait des chemins natifs Windows.
La pratique qui a sauvé la mise était ennuyeuse : emplacement de workspace imposé et rituel diagnostique court. Ils ont corrigé le doc d’onboarding, re-cloné les repos dans WSL, et l’incident s’est éteint discrètement. Rien d’héroïque. C’est le meilleur genre de résultat opérationnel.
Faits intéressants et contexte historique (8 points)
- Le modèle original de Docker supposait un noyau Linux. Le support Windows est arrivé plus tard et a nécessité soit des conteneurs Windows soit une VM Linux pour les conteneurs Linux.
- Docker Desktop sur Windows exécute couramment des conteneurs Linux dans WSL2. C’est pourquoi les sémantiques Linux sont « réelles » à l’intérieur de la VM mais deviennent une « traduction » lorsque vous touchez des fichiers Windows.
- WSL1 et WSL2 sont fondamentalement différents. WSL1 était une couche de traduction d’appels système ; WSL2 est un vrai noyau Linux dans une VM légère, ce qui change drastiquement le comportement des systèmes de fichiers.
- Les permissions NTFS sont basées sur des ACLs, pas sur des bits de mode. Le mappage vers les bits
rwxest approximatif, et les outils supposant des sémantiques POSIX peuvent être trompés. - La sensibilité à la casse sur Windows a évolué. Windows peut activer la sensibilité à la casse par répertoire, mais beaucoup d’outils supposent encore le comportement insensible par défaut sur NTFS.
- La surveillance de fichiers est un piège de portabilité. Inotify est natif Linux ; Windows a des API différentes ; traverser ces couches pour transmettre les événements est imparfait et, malgré des améliorations, reste une source courante de douleurs pour le dev.
- Les bind mounts ne sont pas « des volumes mais plus simples ». Ils héritent des sémantiques du système hôte, ce qui est génial sur Linux et compliqué sur Windows/macOS.
- Les volumes Docker nommés sont souvent plus rapides parce qu’ils restent dans le système de fichiers Linux. Moins de frontières traversées signifie moins de surprises et moins de falaises de performance.
Listes de contrôle / plan étape par étape
Checklist A : Nouveau laptop Windows, configuration Docker dev la moins contraignante
- Activez WSL2 et installez une distribution Linux.
- Installez Docker Desktop et assurez-vous qu’il utilise le moteur WSL2.
- Dans WSL, créez un répertoire de travail :
mkdir -p ~/work. - Clonez les dépôts dans
~/work, pas dans/mnt/c. - Décidez de la politique UID/GID : le conteneur s’exécute avec votre UID/GID pour le dev.
- Mettez à jour Compose : bind mount de la source, volumes pour caches et données.
- Ajoutez un test rapide : le conteneur crée un fichier dans le bind mount et la propriété est correcte.
Checklist B : Patterns de fichiers Compose qui réduisent la douleur
- Bind mount : seulement le code source et la configuration minimale.
- Volumes : répertoires de dépendances, sorties de build, données BD, caches de paquets.
- Mappage utilisateur : définissez
user: "${UID}:${GID}"pour les services de dev qui écrivent sur des mounts. - Entrypoints : évitez les scripts qui supposent que
chmodfonctionne sur les bind mounts. - Health checks : si votre appli écrit de l’état, vérifiez qu’elle peut écrire au démarrage et échouez bruyamment si ce n’est pas le cas.
Checklist C : Quand vous devez garder le repo sur C:\
- Acceptez que chmod/chown puisse être peu fiable ; concevez autour de cette contrainte.
- Exécutez les conteneurs en non-root si possible pour éviter les artefacts root.
- Déplacez tous les chemins à forte écriture dans des volumes.
- Si la surveillance de fichiers est instable, utilisez des watchers en polling et documentez le compromis.
- Gardez vos « tests de correction bind mount » dans la CI ou les scripts d’onboarding pour détecter la dérive tôt.
FAQ
1) Pourquoi chmod ne fonctionne-t-il pas sur mon bind mount depuis Windows ?
Parce que le système de fichiers sous-jacent est NTFS exposé via une couche de traduction. Les bits de mode ne sont pas un concept natif là‑bas, ils peuvent donc ne pas persister ou être émulés.
2) Exécuter le conteneur en root est-il la solution la plus simple ?
C’est la façon la plus simple d’introduire un autre ensemble de problèmes. Vous obtiendrez des artefacts possédés par root, un nettoyage déroutant, et parfois les mêmes échecs d’écriture sur des mounts Windows.
3) Quelle est la façon la plus propre d’éviter les fichiers possédés par root ?
Exécutez le conteneur avec votre UID/GID WSL et gardez l’espace de travail dans le système de fichiers WSL. Cette combinaison se rapproche le plus d’un comportement « comme sur Linux ».
4) Dois‑je utiliser des bind mounts ou des volumes pour les bases de données sur Windows ?
Des volumes. Toujours. Les bases de données supposent certaines garanties et caractéristiques de performance que les bind mounts Windows ne fournissent pas de façon fiable.
5) Je dois éditer des fichiers avec des applications Windows. Puis-je garder le repo dans WSL ?
Oui, tant que votre éditeur supporte l’intégration WSL ou l’accès distant au système de fichiers. C’est la voie moderne et supportée pour du développement sérieux sur Windows avec des conteneurs Linux.
6) Mon build est lent mais les permissions semblent correctes. Que faire ?
Faites un benchmark I/O petits fichiers sur le bind mount versus un volume (Tâches 11 et 12). Si le volume est beaucoup plus rapide, déplacez les caches et répertoires de dépendances dans des volumes.
7) Pourquoi la surveillance de fichiers / hot reload échoue uniquement sur Windows ?
Parce que les API d’événements fichiers diffèrent et les événements doivent traverser des couches (Windows ↔ WSL2 ↔ conteneur). Plus vos fichiers sont éloignés du système de fichiers natif du conteneur, plus vous risquez des lacunes des watchers.
8) Puis‑je « corriger » cela avec un seul réglage Docker Desktop ?
Pas de façon fiable. Vous pouvez améliorer les choses par configuration, mais la correction la plus efficace est architecturale : gardez la source dans le système de fichiers WSL et utilisez des volumes pour les chemins fortement écrits.
9) Et si mon équipe utilise des OS hôtes mixtes ?
Standardisez le comportement utilisateur du conteneur et la disposition du stockage. Votre Compose ne doit pas supposer des sémantiques Linux si les hôtes Windows sont prioritaires. Documentez l’exigence d’un workspace WSL.
Conclusion : prochaines étapes concrètes
Si vous voulez la solution « la moins contraignante », arrêtez de négocier avec NTFS pour obtenir des sémantiques Linux. Placez le dépôt dans le système de fichiers Linux de WSL2, faites vos bind mounts depuis là, et exécutez les conteneurs de dev avec votre UID/GID. Ensuite, utilisez des volumes nommés pour tout ce qui écrit beaucoup.
Étapes concrètes :
- Vérifiez le chemin de votre dépôt. S’il est sous
/mnt/c, re-clonez dans~/work. - Définissez
user: "1000:1000"(ou votre UID/GID réel) dans Compose pour les services de dev qui écrivent sur des mounts. - Déplacez les répertoires de dépendances et caches dans des volumes et relancez votre build. Comparez les chiffres des Tâches 11 et 12.
- Ajoutez un petit script smoke test à l’onboarding : créer un fichier dans le bind mount et vérifier la propriété et la possibilité d’écriture.
Vous n’essayez pas de gagner un bras de fer avec Windows. Vous essayez d’exécuter des workflows de développement de qualité production sur un laptop. Rendez la disposition du stockage ennuyeuse, et le reste suivra.