Web Development 10:00 am

DDEV: The Docker Development Environment That Actually Works

Docker made simple. DDEV delivers on the promise of containerized development without the pain.

5 min read

Why I Stopped Fighting My Local Dev Environment

I’m going to tell you about my local development history because it will make DDEV’s existence feel like a miracle by comparison.

For years - years - I edited WordPress files directly on production servers. In GoDaddy’s file manager. A tiny modal window with no line numbers, no syntax highlighting, no undo, and no dignity. I’d make a change, hit save, and refresh the live site to see if it worked. If it didn’t, I’d fix it fast and hope nobody noticed. This was my workflow for longer than I’m comfortable admitting.

Then I graduated to MAMP. Which worked great until it didn’t, which was usually after a macOS update, at which point PHP would break, MySQL would refuse to start, and I’d spend half a day reconfiguring something that was supposed to save me time. I tried XAMPP. I tried Local by Flywheel. I tried Docker Compose with a hand-written docker-compose.yml that I copied from a blog post and understood maybe 40% of.

Every one of these tools solved some problems and created new ones. The cognitive overhead of just getting a WordPress site running locally was enough to make me consider going back to the GoDaddy file editor. At least that worked consistently (in the same way that a hammer consistently drives nails - technically functional, absolutely brutal).

Then Someone Said “Just Use DDEV”

I don’t remember who told me about DDEV. I do remember being skeptical, because at that point I had been burned by every local development tool in existence and my trust was broken. But I have a terminal case of FOMO when it comes to dev tools, so I installed it anyway.

The 211j site you’re reading this on runs on DDEV locally. So do the other WordPress sites I manage. Here’s what the setup looked like:

brew install ddev/ddev/ddev
mkdir 211j && cd 211j
ddev config --project-type=wordpress --docroot=wordpress
ddev start

That’s it. I had a fully functional WordPress environment with SSL, proper URLs, a MariaDB database, and nginx - all running in Docker containers that I didn’t have to configure. No Dockerfile. No docker-compose.yml. No hour of my life sacrificed to the networking gods.

What Actually Makes It Good

I host a small roster of clients on my Linode server. Several of those sites have local DDEV environments. Each one has its own PHP version, its own database, its own configuration. They don’t conflict. They don’t share resources in weird ways. When I ddev start a project, it picks up exactly where I left off.

The WP-CLI integration is seamless. ddev wp plugin list, ddev wp search-replace, ddev wp db export - it all just works. I don’t have WP-CLI installed globally. I don’t need to. DDEV handles it inside the container.

Database management is trivial. ddev import-db < backup.sql to import. ddev export-db to dump. ddev mysql to get a shell. ddev snapshot to save the current state before doing something stupid (which I do regularly). The snapshot feature alone has saved me more times than I want to count.

And Mutagen. Oh, Mutagen. If you've used Docker on a Mac, you know about the file system performance problem. Docker's volume mounts on macOS are slow. Painfully slow. Page loads that should take 200ms take 3 seconds. DDEV uses Mutagen to sync files instead of mounting them, and the performance difference is like going from dial-up to fiber. It's not subtle.

The .ddev Folder Lives in Git

This is the part that makes DDEV work for teams and for your future self. The .ddev/config.yaml file that defines your environment lives in your project repository. PHP version, database type, docroot, custom configuration -- all version controlled. When you clone the repo on a new machine, ddev start gives you the exact same environment. No setup documentation. No "follow these 14 steps." Just start.

I work on a team at my day job. The "it works on my machine" conversation is one of the most expensive conversations in software development. DDEV eliminates it. Everyone gets the same PHP version, the same database, the same nginx configuration. The environment becomes a solved problem.

Custom Commands and Hooks

DDEV lets you add custom commands that run inside the container. I have commands for pulling production databases, running build scripts, and clearing WordPress caches. They live in .ddev/commands/ and they're available to anyone who clones the project.

There are also hooks -- post-start, post-import-db, pre-composer -- that let you automate setup tasks. My typical post-start hook runs a search-replace on the database to fix URLs and flushes the WordPress cache. Every time I start the environment, it's ready to use. No manual steps.

The Parts That Aren't Perfect

DDEV runs on Docker. Docker uses resources. On an older MacBook, you'll feel it. This isn't a DDEV problem specifically -- it's a Docker-on-Mac problem. But it's real. If your machine has 8GB of RAM, running three DDEV projects simultaneously is going to be tight.

Mutagen sync can occasionally get stuck. It's rare, but when it happens, ddev mutagen reset fixes it. I've had maybe three sync issues in a year of daily use. Annoying when it happens, but not a dealbreaker.

The learning curve exists if you want to customize beyond the defaults. Adding custom nginx configurations, extra services like Redis, or specific PHP extensions requires understanding DDEV's configuration system. The docs are good, but there's still a curve.

Why It Actually Changed My Workflow

Here's the honest truth: DDEV is the first local development tool I've used where I stopped thinking about local development. That sounds like a small thing. It's not. Every hour I used to spend fixing MAMP, configuring Docker, or troubleshooting environment issues is now an hour I spend writing code. Over a year, that's weeks of reclaimed time.

When I onboard a new client site, I pull the database, drop it into DDEV, run a search-replace, and I'm developing locally in under five minutes. When I want to test a plugin update before pushing it to production, I snapshot the database, run the update, and test. If it breaks, I restore the snapshot. The whole process is fast enough that I actually do it instead of just yelling "YOLO" and updating on production (which, again, I did for years -- don't be like past me).

If you're still editing files on a live server, if you're fighting with MAMP, if you've been meaning to set up Docker but the configuration files make your eyes glaze over -- just install DDEV. It takes five minutes. It will work. And you'll wonder why you waited so long, the same way I did.