Local Magento development without containers means hand-maintaining PHP, MySQL, Elasticsearch, Redis and Varnish on every developer’s laptop - a recipe for “works on my machine”. Docker fixes that by making the environment code. Here is how to run Magento 2 locally in Docker without losing your sanity, especially on macOS.
The Stack You Need
A working Magento 2 local stack is more than PHP:
- PHP-FPM (version matching production) + Nginx or Apache
- MySQL/MariaDB matching production’s major version
- OpenSearch or Elasticsearch - mandatory since 2.4
- Redis for cache and sessions
- Optionally RabbitMQ, Varnish, Mailhog for SMTP capture
A compose file wiring these together is the baseline; matching versions to production is the part people skip and later regret.
markshust/docker-magento vs Rolling Your Own
The community standard is markshust/docker-magento: battle-tested, one-command setup, Xdebug preconfigured, actively maintained. For most teams it is the right choice - the time to build a custom stack is when you have requirements it genuinely cannot meet, not before. If you do build custom, keep it boring: official images, pinned versions, minimal build steps.
macOS Performance: The Real Problem
Docker on Mac pays a filesystem tax: bind-mounted Magento’s tens of thousands of PHP files are brutally slow. The fixes, in order of impact:
- Use delegated/cached mounts or, better, a volume-sync strategy - the markshust setup’s mutagen-style sync removes the bind-mount penalty
- Exclude
vendor,generated,var,pub/staticfrom heavy sync; they churn constantly - Give Docker Desktop real resources: 4+ CPUs, 8GB+ RAM minimum for a Magento stack with OpenSearch
On Linux, native bind mounts are fast and none of this applies - one of the genuine advantages of a Linux laptop for Magento work.
Xdebug Without the Pain
Xdebug running permanently makes every request crawl. The sane pattern: Xdebug installed but disabled by default, enabled per request via browser extension or environment toggle. Configure xdebug.mode=debug only in a dev override file, and set log_level so you can see when it is actually connected.
Team Onboarding
The payoff for all of this: a new developer runs git clone, one setup command, and has a working store with sample data in twenty minutes. Document the three commands in the README, keep the compose file in the repo, and treat environment drift as a bug - if production upgrades PHP, the compose file upgrades the same week.
Local environment quality is a force multiplier: every hour not spent fighting a broken laptop setup is an hour spent on the work clients pay for. Docker, used with the performance fixes above, buys those hours back reliably.