Quand on déploie une application web avec Docker, surtout une stack Laravel + PHP-FPM + Nginx, la gestion du stockage persistant devient rapidement un sujet critique.
Entre volumes managés et bind mounts, le mauvais choix peut provoquer :
- des problèmes de permissions,
- des conflits entre utilisateurs Docker et système,
- des fichiers invisibles pour Nginx,
- ou encore des performances dégradées.
Après plusieurs architectures testées en production, voici le meilleur compromis que j’ai trouvé.
Comprendre les deux types de volumes Docker
1. Bind Mounts
Le bind mount lie directement un dossier de la machine hôte au container.
volumes:
- ./project:/var/www
Avantages :
- Parfait pour le développement
- Accès direct aux fichiers depuis l’hôte
- Hot reload immédiat
- Facile pour Git
Inconvénients :
- Permissions souvent compliquées entre les utilisateurs du container docker et l’hôte
- Conflits UID/GID
- Performances parfois moins bonnes
- Dépendance à la structure de l’hôte
2. Volumes managés Docker
Docker gère lui-même le stockage.
volumes:
laravel_storage:
services:
app:
volumes:
- laravel_storage:/var/www/storage
Avantages :
- Permissions plus propres et géré par docker
- Isolation parfaite
- Plus fiable en production
- Meilleures performances Linux
Inconvénients :
- Moins accessible directement
- Toujours penser à mettre à jour pendant le déploiement
- Debug moins pratique
Le piège classique avec Laravel
Laravel écrit énormément dans :
storage/
bootstrap/cache/
Exemples :
- logs
- cache
- sessions
- uploads
- images générées dynamiquement
Si ces dossiers sont en bind mount :
./storage:/var/www/storage
alors PHP écrit avec son user Docker (souvent www-data), mais l’hôte possède souvent un autre UID.
Résultat :
- fichiers root:root
- fichiers www-data:33
- fichiers user local:1000
Et là commencent les problèmes :
Permission denied
Failed to open stream
Unable to write file
Nginx 403 forbidden
Le gros problème : Nginx doit voir les fichiers statiques
Prenons ce cas réel :
- Laravel génère une image :
storage/app/public/generated/image.png
- Le symlink public existe :
public/storage -> ../storage/app/public
- Nginx sert :
/var/www/public/storage/generated/image.png
Si Nginx et PHP n’utilisent pas exactement le même volume :
- PHP voit le fichier
- Nginx ne le voit pas
Résultat :
404 Not Found
L’architecture recommandée
Après plusieurs essais, voici l’architecture la plus stable.
Le code source → Bind Mount
./src:/var/www
Pourquoi ?
- Git fonctionne normalement
- déploiement simple
- édition facile
Les dossiers dynamiques → Volumes managés
volumes:
laravel_storage:
laravel_cache:
services:
app:
volumes:
- ./src:/var/www
- laravel_storage:/var/www/storage
- laravel_cache:/var/www/bootstrap/cache
Pourquoi ?
- Laravel écrit librement
- pas de conflit de permissions
- plus sécurisé
Nginx monte aussi storage
services:
nginx:
volumes:
- ./src/public:/var/www/public
- laravel_storage:/var/www/storage:ro
Ainsi :
- PHP écrit
- Nginx lit
- les fichiers générés sont visibles immédiatement
Pourquoi ne pas bind mount "storage" ?
| Critère | Bind Mount | Managed Volume |
|---|---|---|
| Facilité debug | ✅ | ❌ |
| Permissions propres | ❌ | ✅ |
| Production stable | ❌ | ✅ |
| Compatibilité multi-user | ❌ | ✅ |
| Nginx lecture facile | Moyen | ✅ |
Exemple docker-compose recommandé
services:
app:
build: .
volumes:
- ./src:/var/www
- laravel_storage:/var/www/storage
- laravel_cache:/var/www/bootstrap/cache
nginx:
image: nginx:alpine
volumes:
- ./src/public:/var/www/public:ro
- laravel_storage:/var/www/storage:ro
volumes:
laravel_storage:
laravel_cache:
Règle simple à retenir
- Code source : bind mount
- Uploads utilisateurs : volume managé
- Cache : volume managé
- Logs : volume managé
- Images générées : volume managé partagé avec Nginx
- Configs : bind mount
Conclusion
Si tu développes en Laravel avec Docker :
Le meilleur compromis est hybride.
Utilise les bind mounts pour tout ce qui est source code et configuration, et des volumes managés pour tout ce que Laravel écrit lui-même.
Et surtout :
si Nginx doit servir un fichier généré par Laravel, ce dossier doit être monté dans les deux containers.
C’est ce point qui évite 80% des problèmes de fichiers statiques en production.