Ultimate Docker Desktop Performance Guide for Mac in 2025

Ultimate Docker Desktop Performance Guide for Mac in 2025

Learn how to get 80% faster Docker performance on Mac with selective volume mounting strategies that work on both Intel and Apple Silicon.

The fastest Docker Desktop setup for Magento on a Mac mounts only what you edit (app/code, app/design, app/etc and a few config files) and keeps vendor, pub and everything else in a named volume. On a 2.4.8 install, that took cold page loads from 20-30 seconds down to 3-4.

Mounting your whole Magento codebase into Docker Desktop looks like the simple setup. It's the slowest one you can pick, and even that M4 chip isn't going to save you from a bad volume mount strategy.

The problem that won't die

I first wrote about this problem back in 2018. Since then, Docker Desktop has gone through multiple rebrandings, Apple silicon changed the game, VirtioFS became a thing, and Mutagen showed up (I was a very early tester of it, but it ultimately failed the resilience test for me).

And yet... developers are still mounting their entire source directory and wondering why a 100,000-file project crawls.

The fundamental issue hasn't changed: Docker Desktop on macOS runs through a virtualization layer. Every single file read and write has to cross that boundary. When you're dealing with frameworks like Magento that touch thousands of files per request, that overhead compounds fast.

Why your current setup is killing performance

Here's what most developers do:

volumes:
  - ./src:/var/www/html:cached # RIP your performance

Simple, clean, everything syncs.

And it's also absolutely terrible for performance.

On a typical Magento install, this means Docker is syncing:

  • 100,000+ files and folders
  • Generated cache directories that rebuild constantly
  • Vendor folders with thousands of dependencies
  • Temp files you'll never look at
  • Static assets that get compiled and recompiled

Your page load times could be 20-30 seconds with a cold cache, maybe even more. Even with all the Docker Desktop optimizations available, and even with Apple silicon, performance can still drag.

But it's not just Magento. Symfony, Laravel, WordPress with tons of plugins... any framework with a large codebase and lots of file operations suffers the same fate.

And it has to do with how volume mounts work.

The solution: selective volume mounting

You don't need to mount everything. Docker's own docs say the same thing: share only the directories you need, and keep non-code items like caches and databases in the Linux VM, in a data volume.

Think about it. When you're developing on Magento, where do you actually write code?

For most of us, it's basically just in the app/ directory. Everything else is library code, generated files, caches and vendor packages. It's all of the stuff you never touch.

So why are you syncing it?

Here's the approach that works (and the one we run in docker-magento):

services:
  app:
    volumes: &appvolumes
      ## Host mounts with performance penalty, only put what is necessary here
      - ./src/app/code:/var/www/html/app/code:cached
      - ./src/app/design:/var/www/html/app/design:cached
      - ./src/app/etc:/var/www/html/app/etc:cached
      - ./src/composer.json:/var/www/html/composer.json:cached
      - ./src/composer.lock:/var/www/html/composer.lock:cached
      - ./src/grunt-config.json.sample:/var/www/html/grunt-config.json:cached
      - ./src/Gruntfile.js.sample:/var/www/html/Gruntfile.js:cached
      - ./src/dev/tools/grunt/configs:/var/www/html/dev/tools/grunt/configs:cached
      - ./src/nginx.conf.sample:/var/www/html/nginx.conf:cached
      - ./src/package.json.sample:/var/www/html/package.json:cached
      - ./src/generated:/var/www/html/generated:cached
      - ./src/var:/var/www/html/var:cached
      #- ./src/auth.json:/var/www/html/auth.json:cached
      #- ./src/m2-hotfixes:/var/www/html/m2-hotfixes:cached
      #- ./src/patches:/var/www/html/patches:cached

We are only mounting the directories that we are actively working in, plus two deliberate exceptions (more on those below). Everything else is kept in native Docker volumes, which is an order of magnitude faster because the files and folders never cross the virtualization boundary.

A quick note on :cached and :delegated: those consistency flags date back to the osxfs days. Docker now only describes consistency as "platform specific", so don't count on either one to speed up a VirtioFS mount. docker-magento uses :cached, so I use it everywhere here.

The folders to keep out of your mounts

Here are the directories that are performance killers. Keep them in native Docker volumes:

  • node_modules/

    • JavaScript developers, this one's for you. Massive folder, never edited directly, absolutely murders performance when mounted.
  • pub/

    • pub/static rebuilds constantly. Every request potentially touches hundreds of files here. Native Docker volume only!
  • vendor/

    • This is the big one. Thousands of files, tons of reads, zero edits. Your Composer dependencies don't need bidirectional sync. If you're debugging a vendor package, mount that specific path temporarily.
  • var/ and generated/ (with an asterisk)

    • Cache files, sessions, logs and interceptors. Heavy writes and heavy reads. docker-magento mounts both anyway, on purpose: your IDE needs generated/ to resolve factories, proxies and interceptors while you step through code with Xdebug, and having var/log and var/report on the host means you can open logs without executing commands in your container. That's a trade-off I'm happy with. If you never look at either, drop those two lines and get a little more speed back.

The setup process

"But Mark, how do I get my files into the container if I'm not mounting them?"

Well, most of the time you will never need to, because your main code lives in app/ (or equivalent) and is bi-directionally synced.

But for the rest, you can just copy them over when needed. I've set up helper copy commands in docker-magento for this:

# Copy everything into the container initially
bin/copytocontainer --all
 
# Or if you need to update vendor after a composer install on the host
bin/copytocontainer vendor
 
# And pull something back out of the container
bin/copyfromcontainer vendor

Outside of docker-magento, plain docker cp does the same job. If you're new to Docker, you might want to brush up on how to run containers first, but the process is straightforward (swap phpfpm for whatever your PHP service is called):

docker cp ./src/. "$(docker compose ps -q phpfpm)":/var/www/html/

Real world performance numbers

Let me give you some real numbers from a Magento 2.4.8-p2 install with 100,000+ files:

Full mount approach: 20-30 seconds page load (cold cache)
Selective mounting: 3-4 seconds page load (cold cache)

That's over an 80% performance improvement... just by being smart about what you mount!

With warm cache, the difference is even more dramatic. Full mounting might get you down to 5-6 seconds, while selective mounting will get you down to sub-second responses.

Which Docker Desktop settings matter for Magento?

These are the settings you'll actually see in Docker Desktop today, under Settings > General and Settings > Resources:

  • Virtual Machine Manager: the Apple Virtualization framework (the default), or Docker VMM. Docker VMM is still in Beta, Apple silicon only, and supports VirtioFS only. Worth a try, but I wouldn't build a team setup on a Beta.
  • File sharing implementation: VirtioFS (the default) or gRPC FUSE. This choice only shows up with the Apple Virtualization framework. Stick with VirtioFS.
  • Synchronized file shares: builds a synced ext4 cache of a host folder inside the VM, and Docker aims it squarely at repositories with 100,000 files or more. It's the closest built-in answer to this whole article. The catch: it needs a Pro, Team or Business subscription, and it's one more sync layer that can drift. If you have the subscription, it's worth testing against selective mounting on your own project.
  • Memory: give Docker at least 6GB. Magento, OpenSearch and the database will happily eat all of it.

Modern alternatives (and why they're not silver bullets)

VirtioFS: Much better than the old osxfs, sure. But it's still not native speed. Maybe you get a 20-30% improvement, but not the 80% you get from selective mounting.

Mutagen: Adds complexity, requires daemon management, and can get out of sync (which is why I've never ported it into docker-magento). Great for some workflows for projects with not many files, but it's overkill for most. Plus, now you're debugging sync issues instead of just writing code.

OrbStack/Colima: Different Docker Desktop alternatives. Better in a lot of ways, but the fundamental problem with volume mounting remains. Physics is physics.

The selective mounting approach works everywhere, with no extra tools and no subscription.

Implementation strategy for existing projects

If you're already deep into a project with full mounting, here's how to transition:

First, identify what you actually edit. You'll want to inspect your container details to understand what's currently mounted, then run this in your project:

# Find files you've modified in the last 30 days
find ./src -type f -mtime -30 -not -path "*/vendor/*" -not -path "*/generated/*" -not -path "./src/var/*"

Chances are, it's a tiny fraction of your codebase.

Next, update your compose.yaml (or docker-compose.yml for older setups) gradually:

volumes:
  # Start with a named volume
  - appdata:/var/www/html
 
  # Add selective mounts for your active development areas
  - ./src/app/code/YourVendor:/var/www/html/app/code/YourVendor:cached
 
  # Keep adding as needed

Then, sync your initial data. If you need a refresher on working with Docker images and containers, check out the Docker Core Fundamentals playlist:

# One-time sync to populate the named volume
docker cp ./src/. "$(docker compose ps -q phpfpm)":/var/www/html/

The docker-magento implementation

If you're using my docker-magento repo, you might have noticed this selective sync approach is already built in. The setup includes this exact strategy by default, which is one of the reasons it performs so much better than a vanilla Docker setup. (Setting it up from scratch is covered in setting up a Magento 2 development environment with docker-magento.)

The automated setup script handles the initial file copy, sets up the selective mounts, and manages the sync process. This is based on my volume mount approach for performance that I've been refining for years.

If you live in PhpStorm or VS Code, there's also an SSH/SFTP mode (compose.dev-ssh.yaml, since v40). It keeps every file in the appdata volume and syncs through your IDE's SFTP deployment instead, so you can work on vendor and generated at full speed.

The bottom line

Stop fighting Docker Desktop performance or waiting for the perfect solution. And definitely stop mounting your entire codebase!

Mount just what you edit, and keep everything else native. Your CPU and battery will thank you, and you might actually enjoy local development with Docker again.

It's also important to mention that this isn't a hack or a workaround! This is understanding how Docker volumes actually work and using them correctly. The fact that this approach from 2018 still works better than most "modern" solutions should tell you something.

Your M4 Mac is fast. Docker Desktop has improved drastically over the years. But crossing virtualization boundaries still has overhead.

Need to check if a file exists in vendor? Use docker exec to execute commands inside your container.

Need to debug generated code? Copy it out temporarily.

Need to edit a third-party module? Mount that specific path for the debugging session, then unmount it.

The key is being intentional about what crosses the virtualization boundary. Every mounted file is a potential performance hit, so make each one count. If you want the whole setup walked through end to end, the Magento 2 Development Environment course is open access and already in your Library account.

Something here out of date?

Magento moves, and so does this site. If a step no longer works, tell me and I'll fix it.

Get the next one by email

Every other week I send a short newsletter on how PHP + Magento actually work: releases, security alerts, and one thing explained under the hood. Enroll in the Library and it's yours. No credit card needed.

The University includes every PHP + Magento course available, and every new M.academy course as it ships.