Docker for Teams: How We Cut Onboarding from 3 Days to 2 Hours

"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.

The Real Cost

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:

  1. Install Docker Desktop
  2. Clone the repository
  3. Run ./start.sh
  4. Run ./seed.sh
  5. 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