"It works on my machine."
If you've worked on a development team for more than a week, you've heard this phrase. Probably said it yourself. I certainly have—usually while watching a colleague struggle to reproduce a bug I couldn't see on my setup.
Our team was drowning in environment issues. New developers spent three days (sometimes more) just getting their local environment working. "Works on my machine" wasn't a joke—it was a daily excuse for broken deployments.
Then we containerized everything. Now onboarding takes two hours, and environment bugs have essentially disappeared.
Here's exactly how we did it.
The Problem We Had
Our stack was fairly typical: WordPress with a custom theme, MySQL, Redis for caching, and some Node.js build tools. Nothing exotic.
But every developer's machine was different:
- Different PHP versions (7.4, 8.0, 8.1—all in active use)
- Different MySQL versions and configurations
- Different Node versions (some on 14, some on 16, one guy still on 12)
- macOS vs Windows vs Linux quirks
- MAMP vs XAMPP vs Homebrew vs native installations
Our onboarding doc was 47 steps long. It still missed edge cases. Every new hire discovered new ways for the setup to fail.
We calculated that environment issues cost us roughly 8 hours per developer per month. With 6 developers, that's nearly 600 hours per year—wasted on "works on my machine" problems.
Why Docker?
Docker lets you define your entire development environment in code. Instead of documenting "install PHP 8.1 with these extensions," you write a configuration file that creates an identical environment everywhere.
Key benefits for teams:
- Identical environments: Everyone runs the exact same PHP, MySQL, and Node versions
- Reproducible: "Clone repo, run one command, start coding" becomes reality
- Isolated: Project A's requirements don't conflict with Project B's
- Documented: The Dockerfile IS the documentation
Our Setup (Step by Step)
Step 1: Create the docker-compose.yml
Docker Compose lets you define multiple services (containers) that work together. Here's our configuration:
version: '3.8'
services:
wordpress:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:80"
volumes:
- ./wp-content:/var/www/html/wp-content
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
depends_on:
- db
- redis
db:
image: mysql:8.0
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
redis:
image: redis:alpine
node:
image: node:18-alpine
volumes:
- ./wp-content/themes/our-theme:/app
working_dir: /app
command: npm run watch
volumes:
db_data:
Step 2: Create the Dockerfile
The Dockerfile defines our WordPress environment with all required PHP extensions:
FROM wordpress:php8.1-apache
# Install required PHP extensions
RUN docker-php-ext-install mysqli pdo pdo_mysql
# Install Redis extension
RUN pecl install redis && docker-php-ext-enable redis
# Enable Apache modules
RUN a2enmod rewrite headers
# Copy custom PHP configuration
COPY php.ini /usr/local/etc/php/conf.d/custom.ini
Step 3: Create Helper Scripts
We created simple scripts so developers don't need to remember Docker commands:
# start.sh
#!/bin/bash
docker-compose up -d
echo "Environment running at http://localhost:8080"
# stop.sh
#!/bin/bash
docker-compose down
# logs.sh
#!/bin/bash
docker-compose logs -f wordpress
Step 4: Seed the Database
We created a SQL dump with sample data and a script to import it:
# seed.sh
#!/bin/bash
docker-compose exec -T db mysql -uwordpress -pwordpress wordpress < seed.sql
echo "Database seeded!"
The New Onboarding Process
Our 47-step onboarding doc became this:
- Install Docker Desktop
- Clone the repository
- Run
./start.sh - Run
./seed.sh - Open http://localhost:8080
That's it. From "I don't have access to the repo" to "I'm writing code" in under two hours.
Problems We Solved
1. PHP Version Conflicts
Before: "This code requires PHP 8.1 features but I have 7.4 and can't upgrade because Project X needs 7.4."
After: Each project has its own container with its own PHP version. They don't conflict.
2. Database Drift
Before: "My local database is missing that table/column/data." Constant sync issues between developers.
After: Everyone seeds from the same SQL dump. Need fresh data? Run ./seed.sh.
3. "Works in Production"
Before: Development environments were so different from production that bugs slipped through constantly.
After: Our Docker setup mirrors production. Same PHP version, same extensions, same MySQL version. Bugs found locally stay fixed in production.
4. New Team Member Friction
Before: Senior developers dreaded onboarding because it meant a day of troubleshooting someone else's machine.
After: New hires are self-sufficient. If Docker runs, the project runs.
Lessons Learned
1. Start Simple
Our first docker-compose.yml was minimal—just WordPress and MySQL. We added Redis, Node, and other services incrementally as we validated each worked correctly.
2. Document the Gotchas
Some things still need documentation:
- How to access the database directly (for GUI tools)
- How to run WP-CLI commands inside the container
- How to debug when something goes wrong
Keep a DOCKER.md file with these edge cases.
3. Performance on Mac
Docker's file system performance on macOS can be slow. We solved this with selective volume mounting—only mount directories that actually change frequently (like theme files), not the entire WordPress installation.
4. Team Buy-in Matters
One developer resisted Docker for months, insisting their native setup was "faster." Eventually they came around when they saw how much time everyone else saved. But getting team agreement upfront would have been better.
The Results
Six months after adopting Docker:
- Onboarding time: 3 days → 2 hours
- Environment-related bugs: ~15/month → ~1/month
- "Works on my machine" incidents: Nearly zero
- Developer satisfaction: Significantly higher (no more environment fights)
The initial setup took about a week. It paid for itself within the first month.
Should You Do This?
Docker isn't always the answer. For solo developers on simple projects, it might be overkill.
But if you have:
- A team of 3+ developers
- Multiple projects with different requirements
- Frequent onboarding of new team members
- Regular "it works on my machine" problems
Then Docker will probably save you significant time and frustration. The learning curve is real, but the payoff is worth it.
Need Help Containerizing Your Stack?
I've helped multiple teams adopt Docker for their development workflows. If you're struggling with environment issues or want to streamline your onboarding, let's talk.
Get in Touch