← Tous les articles
June 30, 2026

Docker Permissions : UID, GID, www-data et pourquoi chmod 777 est une mauvaise idée

Développement IOS IT Laravel #sécurité #Docker #docker-compose #Devops

Si tu travailles avec Docker sur une stack Laravel, WordPress, Symfony ou toute autre application PHP, tu as probablement déjà rencontré ce genre d’erreurs :


Permission denied
Failed to open stream
Unable to create cache directory
file_put_contents(): failed to open stream
403 Forbidden

Dans la majorité des cas, le problème ne vient pas de Laravel, PHP ou Nginx. Il vient de la gestion des permissions Linux entre l’hôte et Docker.

Et malheureusement, beaucoup de développeurs utilisent la solution rapide :


chmod -R 777 storage

Ça fonctionne… jusqu’au jour où ça casse, ou pire : jusqu’au jour où ça ouvre une faille de sécurité.

Comprendre les permissions Linux

Chaque fichier Linux possède :

  • un propriétaire (UID)
  • un groupe (GID)
  • des permissions

-rw-r--r--  1 1000 1000 file.txt

Ici :

  • UID 1000 = propriétaire
  • GID 1000 = groupe

Docker ne raisonne pas par nom d’utilisateur. Il raisonne uniquement par UID/GID.

Le piège classique avec Docker

Sur l’hôte :


michael (UID 1000)

Dans le container PHP :


www-data (UID 33)

Si Laravel écrit un fichier dans un bind mount :


storage/app/public/avatar.png

Le fichier sera créé avec :


33:33

Ton hôte peut ne pas avoir accès.

Et inversement :

Si tu crées le dossier depuis ton Mac/Linux :


1000:1000

PHP peut ne pas pouvoir écrire dedans.

Pourquoi chmod 777 est une mauvaise idée


chmod -R 777 storage

Ça donne :

  • lecture à tout le monde
  • écriture à tout le monde
  • exécution à tout le monde

Donc :

  • Nginx peut écrire
  • PHP peut écrire
  • n’importe quel process peut écrire
  • un exploit potentiel aussi

Exemple concret :

Si un upload mal filtré passe :


shell.php

Avec un dossier writable globalement, le risque augmente fortement.

En production, 777 doit rester exceptionnel.

Le problème réel dans notre architecture Laravel + Nginx

Notre architecture ressemblait à ceci :


Host
 ├── src/
 ├── storage/
 ├── docker-compose.yml

Containers:
 ├── app (PHP-FPM)
 └── nginx

Cas réel :

  • Laravel génère une image optimisée
  • stockée dans storage/app/public/generated
  • Nginx doit la servir directement

Problèmes rencontrés :

  • fichier créé par UID 33
  • hôte en UID 1000
  • Nginx parfois autre UID
  • 403 sur certains fichiers
  • symlink cassé visuellement mais existant

Le problème n’était pas Laravel. Le problème était la couche filesystem.

Solution 1 : aligner les UID/GID

Très propre pour le dev.


services:
    app:
        user: "1000:1000"

Résultat :

  • Docker écrit avec le même user que l’hôte
  • moins de conflits

Limites :

  • moins portable
  • pas idéal multi-serveurs
  • plus fragile en prod

Solution 2 : utiliser des groupes partagés

Bonne approche Linux classique.


chgrp -R www-data storage
chmod -R 775 storage

On garde :

  • owner = écriture
  • group = écriture
  • others = lecture uniquement

Beaucoup plus propre que 777.

Solution 3 (notre meilleure architecture) : volumes managés

Celle qu’on a retenue.


services:
    app:
        volumes:
            - ./src:/var/www
            - laravel_storage:/var/www/storage

    nginx:
        volumes:
            - ./src/public:/var/www/public:ro
            - laravel_storage:/var/www/storage:ro

Pourquoi c’est mieux :

  • Docker gère les ownerships
  • pas de conflit hôte/container
  • Laravel écrit librement
  • Nginx lit les mêmes fichiers
  • moins de chmod manuels

Quand utiliser quoi ?


Dossier Type conseillé
Code source Bind mount
.env Bind mount
config/ Bind mount
storage/ Managed volume
bootstrap/cache Managed volume
uploads utilisateurs Managed volume
logs Managed volume

Conclusion

Si tu rencontres des problèmes de permissions avec Docker :

ne commence pas par chmod 777.

Pose-toi d’abord ces questions :

  • Qui crée le fichier ?
  • Quel UID/GID utilise ce process ?
  • Qui doit lire ce fichier ?
  • Est-ce que ce dossier devrait vraiment être un bind mount ?

Dans notre expérience :

code source = bind mount
fichiers dynamiques = volumes managés
nginx + php = partage du même volume

C’est aujourd’hui l’architecture la plus stable, la plus propre et la plus prévisible que nous utilisons.

Thiébault Michaël ©