jobsphp

Volumes Docker sur Mac : pourquoi PHP ralentit

Ingénierie Web. Volumes Docker sur Mac : pourquoi PHP ralentit

Docker Desktop a beau avoir fait d’immenses progrès sur macOS — VirtioFS, virtualisation Apple, améliorations de synchronisation, tout le petit arsenal moderne — il reste un paradoxe que l’on…

Volumes Docker sur Mac: pourquoi PHP ralentit

Docker Desktop a beau avoir fait d’immenses progrès sur macOS — VirtioFS, virtualisation Apple, améliorations de synchronisation, tout le petit arsenal moderne — il reste un paradoxe que l’on rencontre encore tous les jours: une application PHP parfaitement fluide sur Linux devient soudainement poussive sur Mac. Le même dépôt, le même compose.yaml, le même Symfony ou Laravel… et pourtant, chaque requête donne l’impression de traverser une zone 30.

La cause principale n’est généralement ni PHP, ni Docker Compose, ni même votre ordinateur. Le coupable se cache sous le capot: le partage des fichiers entre macOS et la machine virtuelle Linux qui exécute Docker. Et lorsqu’un projet PHP doit ouvrir, parcourir et charger des milliers de petits fichiers, cette couche de communication devient le vrai goulot d’étranglement.

On parle alors de lenteur Docker PHP sur macOS, de volumes Docker qui rament, de partage de fichiers Docker lent pour PHP. Les symptômes sont connus: page qui met plusieurs secondes à s’afficher, commandes Symfony qui semblent figées, tests automatisés qui prennent une pause-café, installation des dépendances qui transforme votre terminal en installation artistique contemporaine.

Le sujet n’est pas nouveau. Mais il reste mal compris, notamment parce que la configuration fonctionne souvent très bien sur Linux. Alors pourquoi cette différence? Et surtout: comment retrouver une expérience de développement digne de ce nom sans abandonner Docker, son réseau, ses services et ses environnements reproductibles?

Pourquoi Docker doit-il passer par une machine virtuelle sur macOS?

Commençons par la base — pas glamour, mais indispensable. Docker ne s’exécute pas nativement sur le noyau de macOS comme il peut le faire sur Linux. Le moteur Docker a besoin des primitives du noyau Linux pour lancer ses conteneurs. Sur un Mac, il fonctionne donc à l’intérieur d’une machine virtuelle Linux légère.

Docker Desktop prend en charge cette couche pour nous. Historiquement, cette virtualisation s’est appuyée sur différentes briques, notamment HyperKit et LinuxKit. Les versions récentes utilisent aussi le framework de virtualisation d’Apple. Dans tous les cas, le résultat est le même pour notre projet PHP: le code qui tourne dans le conteneur n’habite pas exactement au même endroit que les fichiers que l’on édite sur macOS.

C’est là que les choses commencent à se compliquer.

Dans un environnement Linux classique, le moteur Docker, le conteneur PHP et le système de fichiers résident dans une continuité relativement directe. Un fichier est demandé, le système le lit, PHP poursuit son exécution. Sur macOS, la requête peut devoir traverser plusieurs couches:

1. PHP demande un fichier au système de fichiers du conteneur.

2. Le conteneur utilise un répertoire monté depuis l’hôte.

3. Docker doit transmettre cette opération à la machine virtuelle Linux.

4. Le mécanisme de partage fait l’aller-retour entre la VM et macOS.

5. Le fichier revient dans le processus PHP.

Pour un gros fichier lu une seule fois, ce détour peut rester discret. Pour des milliers de petits fichiers, il devient beaucoup plus visible. Or les applications PHP modernes adorent les petits fichiers. Symfony, Laravel, les dépendances Composer, les classes générées, les fichiers de configuration, les métadonnées, les caches: tout ce petit monde sollicite le système de fichiers en permanence.

Un seul accès lent ne change pas votre journée. Des dizaines de milliers d’accès ralentis, eux, changent complètement la sensation de travail.

Sur macOS, le problème n’est pas que Docker soit lent en soi: c’est que PHP lui demande exactement le type d’opérations que le partage de fichiers supporte le moins bien.

Le rôle des bind mounts

Dans un fichier Docker Compose, on monte souvent le projet local dans le conteneur avec une syntaxe de ce genre:

.:/var/www/html

Ce montage — appelé bind mount — permet de modifier un fichier dans votre éditeur macOS et de voir immédiatement le changement dans le conteneur. C’est confortable, intuitif, presque magique. Et comme souvent avec la magie, il y a une facture cachée.

Le conteneur voit un répertoire qui appartient en réalité à l’hôte. Chaque lecture, écriture, vérification de permission ou recherche de fichier peut donc passer par le mécanisme de synchronisation entre macOS et Linux. Plus le projet contient de fichiers et plus le code les parcourt fréquemment, plus le coût s’accumule.

Les projets PHP sont particulièrement exposés pour une raison simple: l’autoloading et les frameworks modernes font beaucoup de travail autour des fichiers. Même lorsque le code applicatif semble modeste, le dossier vendor peut contenir une quantité considérable de classes et de métadonnées. Le conteneur d’injection de dépendances, les caches et les mécanismes de découverte ajoutent encore des accès.

Et non, supprimer trois commentaires dans un contrôleur ne va pas résoudre le problème. Le vieux réflexe consistant à chercher une micro-optimisation dans le code métier alors que le système de fichiers attend à chaque appel, c’est un peu comme alléger une voiture en retirant le porte-gobelet alors que le frein à main est serré.

Pourquoi Symfony et Laravel souffrent-ils autant?

Tous les conteneurs ne ressentent pas la lenteur des montages de fichiers avec la même intensité. Une application qui sert un fichier statique ou exécute un binaire compilé peut rester parfaitement acceptable. PHP, lui, passe son temps à charger du code interprété, à vérifier des chemins et à initialiser tout un écosystème applicatif.

Composer et le dossier vendor

Commençons par Composer. Dans un projet PHP moderne, l’autoloader ne se contente pas de charger deux ou trois classes. Il gère l’ensemble des bibliothèques installées, les espaces de noms, les fichiers de fonctions, les classes préchargées et différentes optimisations d’accès.

Lorsque l’application démarre, PHP peut parcourir de nombreux fichiers dans vendor, charger des définitions et vérifier la présence de classes. Avec un montage de fichiers lent, le temps passé sous le capot augmente rapidement. Et comme ces opérations se répètent souvent pendant le développement, la gêne devient permanente.

Le problème est encore plus visible dans les commandes de la console. Une commande Symfony ou Laravel peut sembler simple depuis le terminal, mais elle démarre généralement le noyau de l’application, charge la configuration, initialise le conteneur de services et déclenche parfois plusieurs mécanismes annexes. Si chaque étape lit des fichiers à travers un bind mount macOS, la commande prend du poids sans que votre code métier ait changé d’une ligne.

Symfony: le cache ne fait pas tout

Symfony met en cache une partie importante de son fonctionnement. C’est une excellente nouvelle, mais il ne faut pas confondre cache applicatif et suppression complète des accès au système de fichiers.

Dans un scénario observé avec Symfony 4 sous Mojave, le rendu initial pouvait atteindre 12 000 millisecondes dans une configuration Docker sans cache. Avec le cache Symfony, le temps descendait à 344 millisecondes. L’utilisation de l’option de montage :cached ramenait encore le résultat à 195 millisecondes. En exécution native, hors Docker, le même scénario atteignait 82 millisecondes.

Ces chiffres ne sont pas une promesse applicable à tous les projets — le nombre de fichiers, la version de PHP, la configuration d’OPcache et le matériel changent évidemment la donne — mais ils montrent l’ordre de grandeur du problème. Le cache applicatif améliore la situation, sans supprimer le coût structurel du partage de fichiers.

Une application Symfony en développement fait par ailleurs beaucoup plus que servir une page. Elle peut regénérer des caches, analyser des annotations ou des attributs, compiler des conteneurs, inspecter des répertoires et recharger une configuration. Le mode développement est conçu pour fournir du retour rapide au développeur, pas pour minimiser chaque accès disque. Sur une couche de fichiers déjà lente, cette transparence devient paradoxalement ce qui se ressent le plus.

Laravel et les milliers de petites lectures

Laravel n’échappe pas à la règle. Le framework s’appuie sur Composer, des fichiers de configuration, des vues, des classes de service, des routes et des caches. À chaque modification, certains mécanismes doivent réévaluer l’état du projet.

Les performances peuvent aussi être affectées par les outils autour de l’application: analyse statique, tests, génération de documentation, découverte de paquets, surveillance des fichiers. Le ralentissement ne vient donc pas nécessairement d’une page web isolée. Il peut apparaître dans l’ensemble du cycle de développement.

C’est pour cela qu’un projet Laravel peut sembler convenable lors d’une simple navigation, puis devenir franchement pénible dès qu’on lance une suite de tests ou une commande qui parcourt une grande partie du dépôt. Le temps de réponse n’est plus seulement celui de PHP: c’est celui de PHP multiplié par une longue série d’opérations de fichiers.

Le cas des tests automatisés

Les tests sont souvent le révélateur brutal de la situation. Une requête HTTP unique peut rester acceptable grâce au cache et à OPcache. Une suite de centaines ou de milliers de tests, elle, répète les démarrages, les chargements de classes, les créations de fichiers temporaires et les accès aux fixtures.

On se retrouve alors avec cette scène familière: le code est propre, les tests sont bien isolés, l’architecture est plutôt élégante… mais la suite met tellement de temps à s’exécuter que personne ne la lance plus avant une fusion. Et une suite que l’on ne lance plus n’est pas un filet de sécurité; c’est un décor.

Quand les tests ralentissent à cause du système de fichiers, on finit par modifier ses habitudes de développement. C’est souvent là que le problème devient architectural, pas seulement technique.

osxfs, gRPC FUSE, VirtioFS: pourquoi le protocole compte-t-il autant?

Le partage de fichiers entre l’hôte et la machine virtuelle n’est pas une abstraction unique et immuable. Docker Desktop a fait évoluer ses mécanismes au fil des années pour réduire la latence des opérations d’entrée-sortie.

L’époque osxfs

Le système osxfs a longtemps été associé aux performances décevantes des bind mounts sur Mac. Il permettait de rendre les fichiers macOS accessibles aux conteneurs Linux, mais le coût de certaines opérations était particulièrement sensible dans les projets contenant beaucoup de petits fichiers.

À cette époque, la différence entre un projet PHP exécuté nativement et le même projet sous Docker pouvait être spectaculaire. Le fameux résultat à 12 000 millisecondes avec Symfony 4 illustre bien ce que l’on pouvait rencontrer dans les cas défavorables. Le terme de partage de fichiers Docker lent pour PHP n’était pas une exagération marketing: c’était une expérience quotidienne pour beaucoup d’équipes.

gRPC FUSE: mieux, mais pas miraculeux

gRPC FUSE a ensuite amélioré la communication entre macOS et la machine virtuelle. Les opérations d’entrée-sortie pouvaient être traitées plus efficacement et les cas les plus pénalisants étaient atténués.

Mais une amélioration du mécanisme ne change pas la nature du problème. Tant que le code réside sur macOS et que le conteneur doit y accéder à travers une VM Linux, il existe un coût de synchronisation. Il peut être faible, raisonnable ou franchement agaçant selon le projet et les opérations demandées.

C’est un point important: changer de moteur de virtualisation ou mettre à jour Docker Desktop ne transforme pas automatiquement un bind mount en volume natif. La pile est meilleure, mais elle n’est pas identique à un système de fichiers Linux situé directement dans l’environnement qui exécute PHP.

VirtioFS: le progrès qui remet les performances dans le débat

VirtioFS constitue une évolution importante. Son objectif est de fournir un partage de fichiers plus efficace entre la machine virtuelle et l’hôte, avec une latence réduite par rapport aux mécanismes historiques.

Son adoption progressive dans Docker Desktop à partir de 2023 a amélioré la situation de nombreux développeurs. Pour certains projets, la différence est immédiatement visible. Les commandes répondent mieux, les pages se chargent plus vite, les outils de surveillance deviennent plus utilisables.

Mais VirtioFS n’élimine pas totalement la latence d’un environnement Linux pur. Il réduit le coût; il ne supprime pas la frontière entre les deux systèmes. Une application qui effectue énormément d’opérations sur de petits fichiers peut encore être pénalisée, même avec une configuration moderne.

La bonne question n’est donc pas: VirtioFS est-il rapide? La bonne question est: VirtioFS est-il suffisamment rapide pour la charge de fichiers de votre projet PHP? Et la réponse dépendra davantage de la structure du dépôt que d’une fiche technique générale.

Volume nommé ou bind mount: faut-il choisir entre vitesse et confort?

Le volume nommé est l’alternative la plus directe au bind mount. Au lieu de monter un dossier macOS dans le conteneur, on utilise un volume géré par Docker et stocké à l’intérieur du système de fichiers de la machine virtuelle Linux.

Le gain peut être important, car les fichiers ne traversent plus en permanence la frontière macOS–Linux. PHP lit alors un système de fichiers beaucoup plus proche de son environnement d’exécution. Les performances peuvent se rapprocher de celles d’une exécution native.

Mais le confort de développement change.

Si votre code est installé uniquement dans le volume nommé, votre éditeur macOS ne le manipule plus directement. Il faut alors synchroniser les fichiers, travailler depuis un environnement distant ou accepter une organisation moins immédiate. On gagne du temps côté conteneur, mais on peut en perdre côté expérience développeur.

Organisation des fichiersRapidité côté PHPConfort d’édition sur macOSCas d’usage naturel
Bind mount classiqueSouvent la plus faible sur les gros projetsExcellentePetit projet, prototypage, besoin de simplicité
Bind mount avec :cached ou :delegatedMeilleure selon le sens des échangesExcellenteDéveloppement local avec peu d’écritures concurrentes
Volume nommé DockerProche du natif dans la VMMoins directeTests, dépendances, caches et services lourds
Synchronisation MutagenGénéralement très bonneBonne après synchronisationProjet PHP important, équipe habituée à l’outillage
NFSVariable, parfois très efficaceBonne selon la configurationEnvironnement maîtrisé et besoins spécifiques
Colima ou OrbStackDépend du moteur et du partage choisiBonne à très bonneAlternative à Docker Desktop
Environnement DDEVOptimisée pour le développement webBonneProjets PHP structurés et équipes recherchant une convention

La solution la plus intéressante consiste souvent à ne pas choisir un seul modèle pour tout. Le code source peut rester synchronisé avec l’hôte tandis que vendor, les caches et les répertoires temporaires résident dans des volumes nommés.

Cette séparation est particulièrement pertinente pour PHP. Le code que l’on modifie doit rester accessible depuis l’éditeur. Les dépendances que l’on ne touche presque jamais, elles, n’ont aucune raison de faire des allers-retours entre macOS et la VM à chaque requête.

Le montage :cached et le compromis de cohérence

Les options :cached et :delegated, introduites dans Docker pour mieux gérer les compromis de synchronisation, peuvent apporter un gain appréciable.

L’idée est de donner une priorité différente à la cohérence des fichiers selon le sens des échanges. Avec :cached, le conteneur peut considérer que la vue des fichiers côté hôte est plus souvent à jour que l’inverse. Avec :delegated, on accepte davantage de latence pour refléter les écritures du conteneur sur l’hôte.

Ces options ne sont pas des accélérateurs universels. Elles modifient le comportement du partage et peuvent être pertinentes lorsque l’on connaît les flux réels du projet. Si une commande écrit un fichier que l’éditeur doit voir immédiatement, un compromis trop agressif peut devenir déroutant.

Dans une application PHP, on peut généralement réserver ces options aux répertoires dont le sens des modifications est clair. Mais il faut éviter d’empiler les drapeaux dans Compose comme des porte-bonheur. Une option mal comprise peut rendre le diagnostic plus compliqué sans résoudre la cause principale.

Quelles stratégies fonctionnent vraiment pour accélérer PHP sur Mac?

Il n’existe pas de bouton magique, malgré les captures d’écran enthousiastes de certains dépôts GitHub. En revanche, plusieurs stratégies sont solides et se combinent bien.

1. Garder vendor dans un volume Linux

C’est souvent le premier changement qui produit un effet concret. Le code source reste monté depuis macOS, mais le dossier vendor est placé dans un volume nommé.

Pourquoi cela aide-t-il? Parce que Composer et PHP lisent constamment les dépendances. Les laisser derrière un bind mount revient à faire traverser la frontière hôte–VM à une quantité énorme de petites opérations.

On peut appliquer la même logique aux répertoires suivants:

  • les dépendances installées par Composer;
  • les caches Symfony ou Laravel;
  • les fichiers temporaires générés pendant les tests;
  • les répertoires de compilation et de métadonnées;
  • certains journaux lorsqu’ils sont très sollicités.

Il faut cependant organiser proprement la synchronisation. Un volume nommé vide au démarrage peut masquer le dossier vendor présent dans l’image ou sur le montage. Le processus d’initialisation doit donc savoir installer ou recopier les dépendances au bon endroit.

2. Utiliser Mutagen pour synchroniser plutôt que monter chaque fichier

Mutagen est une solution souvent retenue par la communauté PHP pour contourner la lenteur des bind mounts. Au lieu de faire passer chaque opération de fichier vers macOS, l’outil synchronise le projet entre l’hôte et un espace situé dans la VM ou dans un volume Docker.

Le code reste éditable sur le Mac, mais l’exécution se fait à partir d’une copie locale du côté Linux. On évite ainsi une longue série d’allers-retours pour chaque lecture.

La synchronisation introduit naturellement une notion de délai. Dans la pratique, il faut comprendre comment sont gérées les exclusions, les conflits et les fichiers générés. vendor, les caches et certains répertoires de construction ne doivent pas nécessairement être synchronisés dans les deux sens. Les exclure peut alléger le trafic et éviter des comportements étranges.

Mutagen demande un peu plus de configuration qu’un simple montage .:/var/www/html. C’est précisément sa limite et son intérêt. On abandonne le minimalisme du tutoriel Docker en cinq lignes pour retrouver un environnement qui tient la route sur un vrai projet.

3. Tester OrbStack ou Colima

Docker Desktop n’est pas l’unique option sur macOS. OrbStack et Colima proposent d’autres façons d’exécuter des conteneurs et de gérer la machine virtuelle Linux.

Les résultats varient selon la version de macOS, le processeur, le moteur de virtualisation, le type de montage et la structure du projet. Il serait donc hasardeux d’annoncer un vainqueur absolu. Le bon réflexe consiste à tester la configuration avec vos commandes réelles: démarrage d’une requête, exécution de la suite de tests, génération du cache, installation de Composer et rechargement après modification.

Les comparaisons théoriques sont utiles pour comprendre les mécanismes. Elles ne remplacent pas une mesure sur votre dépôt. Un projet Laravel avec une grande quantité de dépendances et un projet Symfony optimisé par OPcache ne solliciteront pas le système de fichiers de la même manière.

4. Regarder du côté de DDEV

DDEV propose un environnement dédié au développement web, avec des conventions et des mécanismes d’optimisation qui peuvent simplifier la gestion des projets PHP.

Son intérêt ne se limite pas à la vitesse brute. Il fournit une expérience cohérente pour les services, les bases de données, les certificats, les commandes et la configuration locale. Pour une équipe qui ne souhaite pas maintenir elle-même une collection de scripts Compose, cette couche d’outillage peut faire gagner du temps.

Évidemment, adopter DDEV signifie accepter ses conventions. Ce n’est pas forcément pertinent pour une architecture très personnalisée ou un projet qui doit reproduire exactement une infrastructure existante. Mais pour du développement Symfony, Laravel, Drupal ou d’autres applications web PHP, c’est une piste sérieuse, pas un gadget de plus dans la boîte à outils.

5. Utiliser NFS lorsque l’environnement le justifie

NFS peut également améliorer les performances des montages en proposant une autre stratégie de partage entre macOS et la VM Linux. Cette solution est plus ancienne, mais elle reste utilisée dans certains environnements de développement.

Elle demande davantage de soin: permissions, configuration réseau, stabilité du montage et gestion des fichiers. Un NFS mal réglé peut déplacer le problème au lieu de le résoudre. En revanche, dans une équipe qui maîtrise déjà cette technologie, il peut constituer une option efficace.

Le point commun de toutes ces stratégies est simple: réduire le nombre d’opérations qui traversent la frontière entre macOS et Linux. On ne gagne pas vraiment en demandant à PHP de parcourir moins de code par magie. On gagne en plaçant les fichiers sollicités au plus près du processus qui les lit.

Et si le problème venait aussi de la configuration PHP?

Le partage de fichiers est le suspect numéro un, mais il ne faut pas lui faire porter tous les crimes du quartier. Une configuration PHP peu adaptée peut amplifier la sensation de lenteur.

OPcache en développement

OPcache réduit le coût de compilation des scripts PHP en conservant du code compilé en mémoire. En développement, on active souvent la vérification des changements de fichiers pour éviter de redémarrer le conteneur à chaque modification.

Cette vérification est pratique, mais elle peut provoquer de nombreux accès au système de fichiers. Avec un bind mount lent, son coût devient plus visible. Il faut donc trouver un équilibre entre actualisation immédiate et performance.

Désactiver toutes les vérifications peut accélérer PHP, mais au prix d’un comportement trompeur: vous modifiez un fichier et l’application continue d’exécuter l’ancienne version. Ce n’est pas une optimisation, c’est une invitation à déboguer un fantôme.

Le serveur de développement et les observateurs de fichiers

Symfony CLI, Laravel, les outils JavaScript et les environnements de développement surveillent souvent les modifications. Chaque mécanisme d’observation peut solliciter le partage de fichiers.

Les dossiers générés doivent être exclus lorsque c’est possible. Un cache qui change plusieurs fois par seconde n’a aucune raison d’être observé comme du code source. Les dépendances non modifiées non plus.

L’objectif n’est pas de désactiver aveuglément les outils, mais de leur donner une vue plus raisonnable du projet. Un observateur qui surveille vendor, les logs, les caches et les fichiers temporaires ne fait pas de veille technologique: il fait du cardio.

Mesurer avant de refactorer

Avant de réorganiser votre architecture ou de remplacer votre framework, mesurez les opérations qui ralentissent réellement:

  • temps de réponse d’une page en mode développement;
  • durée d’une commande de console;
  • temps d’installation de Composer;
  • durée de la suite de tests;
  • temps de reconstruction des caches;
  • comportement avec et sans montage du dossier vendor;
  • différence entre un bind mount et un volume nommé.

Le scénario Symfony documenté plus haut donne une indication intéressante: passer de 12 000 à 344 millisecondes simplement grâce au cache change complètement l’expérience, mais reste loin des 82 millisecondes observées en natif. Cela permet de distinguer deux problèmes: le coût du démarrage applicatif et le coût résiduel du partage de fichiers.

Sans cette distinction, on finit par appliquer une optimisation au mauvais endroit. On augmente la mémoire du conteneur alors que le disque attend. On modifie PHP-FPM alors que Composer lit trop de fichiers depuis l’hôte. On ajoute un cache HTTP pour accélérer une commande de console. Le grand classique: traiter le symptôme qui porte le costume le plus technique.

Pourquoi les volumes Docker ne sont-ils pas seulement une question de vitesse?

La performance est le déclencheur, mais l’organisation des volumes touche aussi la qualité du développement. Un environnement local lent pousse les développeurs à contourner les pratiques prévues: moins de tests, moins de nettoyage de cache, moins de vérifications, davantage de commandes lancées directement sur la machine.

On retrouve alors un autre problème bien connu de la communauté PHP: l’environnement local cesse de ressembler à l’environnement de production. Pour gagner quelques secondes, on installe Composer et certains outils sur macOS, on exécute une partie des commandes hors conteneur, puis on découvre plus tard une différence de version, de permission ou d’extension.

Docker est censé fournir une base reproductible. Mais la reproductibilité ne consiste pas uniquement à écrire FROM php:... dans un fichier. Elle dépend aussi de la manière dont le code, les dépendances, les caches et les services sont montés.

Un volume nommé pour vendor peut donc être un choix de cohérence autant qu’un choix de performance. Une synchronisation Mutagen peut rendre l’environnement plus proche de celui de l’équipe Linux. DDEV peut éviter que chaque nouveau développeur réinvente la même configuration avec trois scripts shell et une prière.

La bonne architecture locale doit répondre à trois besoins en même temps:

  • l’éditeur doit voir rapidement les fichiers que l’on modifie;
  • PHP doit lire rapidement les fichiers qu’il charge en boucle;
  • les commandes doivent rester reproductibles pour toute l’équipe.

Ce trio explique pourquoi il n’existe pas une configuration universelle. Un simple bind mount est très confortable, mais pas toujours rapide. Un volume nommé est performant, mais moins direct pour l’édition. La synchronisation offre un compromis puissant, au prix d’un outil supplémentaire.

Quelle configuration choisir pour un projet PHP moderne sur Mac?

Pour un petit projet ou une preuve de concept, le bind mount classique reste parfaitement défendable. Si l’application répond vite et que les tests s’exécutent sans vous faire perdre patience, inutile de déployer une usine à synchronisation. La simplicité a aussi une valeur technique.

Pour un projet Symfony ou Laravel conséquent, avec un dossier vendor volumineux, une suite de tests importante et plusieurs services Docker, une organisation hybride devient souvent plus pertinente:

1. Monter le code source pour conserver l’édition directe depuis macOS.

2. Placer vendor dans un volume nommé ou un répertoire synchronisé côté Linux.

3. Isoler les caches et fichiers temporaires dans des volumes internes.

4. Activer VirtioFS dans Docker Desktop lorsque la version utilisée le permet.

5. Tester :cached ou :delegated selon le sens réel des écritures.

6. Mesurer Mutagen, OrbStack, Colima ou DDEV sur le dépôt concerné.

7. Documenter le choix afin que l’équipe ne transforme pas chaque poste en expérimentation personnelle.

Cette approche ne demande pas de renoncer à Docker Compose, à PHP-FPM, à Nginx ou à votre architecture de services. Elle consiste simplement à arrêter de traiter tous les répertoires comme s’ils avaient les mêmes besoins.

Le code source est vivant et doit circuler avec l’éditeur. Les dépendances sont lourdes et changent moins souvent. Les caches sont jetables. Les logs sont bavards. Les traiter de manière identique est rarement une bonne idée.

Le vrai sujet: où votre application lit-elle ses fichiers?

La lenteur Docker PHP sur macOS n’est pas une fatalité, mais elle ne disparaîtra pas non plus en ajoutant un conteneur de plus. Le problème vient principalement du coût des entrées-sorties lorsque les fichiers sont partagés entre macOS et la machine virtuelle Linux.

Les frameworks PHP modernes rendent ce coût très visible parce qu’ils chargent beaucoup de petites ressources: autoloaders, classes, configurations, caches, dépendances et fichiers générés. Les améliorations successives — osxfs, gRPC FUSE, VirtioFS — ont réduit la douleur, sans rendre les bind mounts macOS équivalents à un système de fichiers Linux natif.

La stratégie la plus efficace consiste à rapprocher les fichiers fréquemment lus de PHP. Volumes nommés, synchronisation Mutagen, NFS, Colima, OrbStack ou DDEV: les outils diffèrent, mais l’idée reste la même. On conserve une expérience d’édition agréable tout en évitant de faire transiter chaque petit accès par la frontière entre les deux systèmes.

Et c’est peut-être la leçon la plus utile pour nos environnements de développement: Docker ne ralentit pas PHP par malveillance. Il révèle simplement une géographie des fichiers que l’on préfère souvent ignorer. Une fois cette géographie comprise, le réglage devient beaucoup moins mystérieux.

Le prochain gain de performance ne viendra probablement pas d’un énième slogan sur le développement local. Il viendra de configurations plus intelligentes, de moteurs de virtualisation mieux intégrés et d’une communauté PHP qui acceptera enfin de parler autant des volumes que du code. Oui, même de vendor. Surtout de vendor, en réalité!

Questions fréquentes

Pourquoi Docker est-il plus lent sur macOS que sur Linux ?
Docker ne s'exécute pas nativement sur macOS mais au sein d'une machine virtuelle Linux. Chaque accès aux fichiers du projet doit traverser cette couche de virtualisation, ce qui crée un goulot d'étranglement pour les applications PHP qui manipulent de nombreux petits fichiers.
Le cache de Symfony ou Laravel suffit-il à résoudre les lenteurs ?
Le cache applicatif améliore nettement les temps de réponse, mais il ne supprime pas le coût structurel lié au partage de fichiers. Même avec le cache, les opérations de lecture et d'écriture continuent de subir la latence imposée par la machine virtuelle.
Quelle est la différence entre un bind mount et un volume nommé ?
Un bind mount lie un répertoire de votre Mac au conteneur, ce qui facilite l'édition mais ralentit les accès. Un volume nommé est stocké directement dans le système de fichiers de la machine virtuelle Linux, offrant des performances proches du natif au prix d'une édition moins directe.
Est-ce que VirtioFS règle définitivement les problèmes de performance ?
VirtioFS améliore l'efficacité du partage de fichiers et réduit la latence par rapport aux anciens mécanismes comme osxfs ou gRPC FUSE. Toutefois, il ne supprime pas totalement la frontière entre macOS et Linux et peut rester insuffisant pour les projets effectuant énormément d'opérations sur de petits fichiers.
Comment optimiser la configuration Docker pour un projet PHP ?
La stratégie recommandée consiste à adopter une approche hybride : monter le code source depuis macOS pour l'édition, tout en isolant les dossiers vendor, les caches et les fichiers temporaires dans des volumes nommés internes au conteneur.