Docker for PHP Developers: Stop the “It Works on My Machine” Problem Forever

calendar_today May 4, 2026
person info@softcrony.com
folder devops
Docker containers representing software containerization

“It works perfectly on my machine.” These six words have caused more client crises, delayed more launches, and cost more money than almost any other problem in software development. Docker eliminates this entirely. Here’s how to use it as a PHP developer — even if you’ve never touched a container before.

The Problem Docker Solves

Without Docker, every developer on your team has a slightly different environment: different PHP version, different MySQL version, different extensions installed. Your local machine runs PHP 8.1, the client’s server runs PHP 7.4. Your colleague is on Windows, you’re on Mac. The staging server has a different timezone. Docker packages your entire environment — PHP version, extensions, web server, database — into a container that runs identically everywhere. Your local machine, staging server, and production server all run the exact same configuration.

Key Concepts in 2 Minutes

  • Image: A blueprint for a container (like a PHP class before instantiation)
  • Container: A running instance of an image (the actual running object)
  • docker-compose.yml: A file that defines all your services (PHP, MySQL, Nginx) and how they connect
  • Volume: A way to share files between your local machine and the container so edits appear instantly

Step 1: Install Docker Desktop

Download Docker Desktop from docker.com for Mac or Windows. On Ubuntu Linux:

sudo apt-get update
sudo apt-get install docker.io docker-compose
sudo usermod -aG docker $USER
# Log out and back in after this

Verify installation: docker --version and docker-compose --version

Step 2: Create Your Project Structure

my-project/
├── docker-compose.yml
├── docker/
│   ├── php/
│   │   └── Dockerfile
│   └── nginx/
│       └── default.conf
├── src/           ← your PHP code lives here
└── mysql/         ← database data (auto-created)

Step 3: The docker-compose.yml File

Create docker-compose.yml in your project root:

version: '3.8'

services:

  # PHP-FPM Service
  php:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
    container_name: php_app
    volumes:
      - ./src:/var/www/html
    depends_on:
      - mysql

  # Nginx Web Server
  nginx:
    image: nginx:alpine
    container_name: nginx_server
    ports:
      - "8080:80"
    volumes:
      - ./src:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php

  # MySQL Database
  mysql:
    image: mysql:8.0
    container_name: mysql_db
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: myapp
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppassword
    ports:
      - "3306:3306"
    volumes:
      - ./mysql:/var/lib/mysql

  # phpMyAdmin (optional but handy)
  phpmyadmin:
    image: phpmyadmin:latest
    container_name: phpmyadmin
    ports:
      - "8081:80"
    environment:
      PMA_HOST: mysql
      PMA_USER: appuser
      PMA_PASSWORD: apppassword

Step 4: Your PHP Dockerfile

Create docker/php/Dockerfile:

FROM php:8.2-fpm

# Install common extensions
RUN apt-get update && apt-get install -y \
    libpng-dev \
    libonig-dev \
    libzip-dev \
    zip \
    unzip

RUN docker-php-ext-install \
    pdo_mysql \
    mbstring \
    gd \
    zip \
    opcache

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

Step 5: Nginx Configuration

Create docker/nginx/default.conf:

server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Step 6: Start Your Environment

# Build and start all services (first time)
docker-compose up --build

# Start in background after the first time
docker-compose up -d

# Stop everything
docker-compose down

# View logs
docker-compose logs -f php

# Run a command inside the PHP container
docker-compose exec php bash
docker-compose exec php composer install

Visit http://localhost:8080 — your PHP site is running. phpMyAdmin is at http://localhost:8081.

WordPress in Docker

For WordPress, simplify even further with the official image:

version: '3.8'
services:
  wordpress:
    image: wordpress:php8.2-apache
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppassword
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - ./wp-content:/var/www/html/wp-content

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppassword
      MYSQL_ROOT_PASSWORD: rootpassword
    volumes:
      - mysql_data:/var/lib/mysql

volumes:
  mysql_data:

Practical Tips for Teams

  • Add docker-compose.yml to Git — every team member gets the same environment with one command
  • Add mysql/ to .gitignore — never commit database files
  • Use .env files for passwords — reference them in docker-compose.yml with ${MYSQL_PASSWORD}
  • Use named volumes for databases — data persists even when containers are stopped

Moving to Production

Docker for development doesn’t automatically mean Docker in production — many teams develop with Docker but deploy to a standard cPanel/VPS. That’s perfectly fine. The consistency benefit is still enormous. If you do want Docker in production, look at Docker Swarm (simpler) or Kubernetes (powerful, complex) for orchestrating multiple containers across servers. — We help development teams set up modern DevOps workflows. If your team is spending too much time on environment issues and manual deployments, talk to us →

Leave a comment