Configuration apparemment banale dans un docker-compose.yml :
volumes:
- ./conf/app.xml:/opt/app/conf/app.xml:ro
Sur ma machine de dev, ./conf/app.xml existe (généré localement par un script de setup). Sur la CI ou un PaaS qui clone le repo, il n’est pas là — gitignored parce qu’il contient un secret. Et là Docker fait un truc subtilement mauvais.
Symptôme
Error response from daemon: failed to create task for container:
unable to start container process: error during container init:
error mounting "/host/path/conf/app.xml" to rootfs at "/opt/app/conf/app.xml":
not a directory: Are you trying to mount a directory onto a file (or vice-versa)?
Check if the specified host path exists and is the expected type
Le message dit « not a directory » mais le piège est l’inverse — c’est un dossier qui est monté sur ce qui devrait être un fichier régulier.
Cause
Quand on déclare un bind mount Docker et que le chemin source n’existe pas sur l’hôte, le daemon Docker crée silencieusement un dossier vide à ce chemin. Il suppose qu’on voulait un dossier. Si la cible côté conteneur est un fichier (parce qu’il existait dans l’image), les types ne matchent pas → l’erreur cryptique ci-dessus.
C’est documenté mais peu mis en avant. Le piège tombe systématiquement en CI/CD où le repo cloné ne contient pas le fichier généré localement.
Solution
Trois patterns propres pour ne plus se faire piéger :
1. S’assurer que le fichier existe avant compose up. Soit le commit (avec placeholder), soit le générer dans une étape pre-deploy maîtrisée.
2. Monter un dossier plutôt qu’un fichier, quand l’app le permet :
- ./conf/:/opt/app/conf/:ro
Si le dossier source manque, Docker auto-crée un dossier vide — l’app tombe alors sur une erreur plus parlante (fichier absent à l’intérieur) plutôt que sur un mount-type-mismatch.
3. Embarquer la conf dans une image custom (Dockerfile FROM + COPY). Plus de bind mount, plus de surprise. La meilleure approche quand le fichier est en pratique versionné dans le repo de l’app.
Lessons learned
- Docker auto-crée un DOSSIER si la source d’un bind mount n’existe pas — le message d’erreur « not a directory: Are you trying to mount a directory onto a file » vient en réalité de la SOURCE manquante côté hôte
- Le mount d’un fichier n’est PAS un « create if missing » : il suppose que le fichier est déjà là
- Ce piège tombe systématiquement en CI/CD quand le repo cloné ne contient pas le fichier (
.gitignore+ secret généré localement) - Préférer une image custom (
Dockerfile+COPY) au bind mount d’un fichier généré : la conf voyage avec l’image, plus de dépendance au filesystem hôte - Si tu DOIS bind-mounter un fichier, fail-fast côté CI :
[ -f conf/app.xml ] || (echo "manquant" >&2; exit 1)avant ledocker compose up