Skip to main content

Course Progress

Loading...

Docker-based Development Workflow

Duration: 50 minutes
Module 4: Session 5.3

Learning Objectives

  • Implement hot-reloading for theme and plugin development
  • Manage environment variables for different stages
  • Set up multi-site development environments
  • Optimize Docker workflow for productivity

Introduction

A well-configured Docker workflow accelerates WordPress development by providing instant feedback, easy environment switching, and team collaboration features.

💡
Key Workflow Benefits
Docker enables hot-reloading, instant environment switching, and perfect replication between development and production environments.

Theme Development Workflow

Setting up an efficient theme development environment with live reloading:

Directory Structure for Theme Development

wordpress-docker/
├── docker-compose.yml
├── .env
├── themes/
│   └── my-custom-theme/
│       ├── style.css
│       ├── index.php
│       ├── functions.php
│       ├── assets/
│       │   ├── css/
│       │   ├── js/
│       │   └── images/
│       ├── template-parts/
│       └── package.json
└── plugins/
    └── my-custom-plugin/

Docker Compose Configuration for Development

# docker-compose.yml
version: '3.8'

services:
  wordpress:
    image: wordpress:6.4-php8.2-apache
    container_name: wp-dev
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: mysql:3306
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass
      # Development settings
      WORDPRESS_DEBUG: 'true'
      WORDPRESS_DEBUG_LOG: 'true'
      WORDPRESS_DEBUG_DISPLAY: 'false'
      WP_ENVIRONMENT_TYPE: 'local'
    volumes:
      # Mount theme for live development
      - ./themes/my-custom-theme:/var/www/html/wp-content/themes/my-custom-theme
      # Mount plugin for live development  
      - ./plugins/my-custom-plugin:/var/www/html/wp-content/plugins/my-custom-plugin
      # Persist uploads
      - wp-uploads:/var/www/html/wp-content/uploads
      # Custom PHP configuration
      - ./config/php/dev.ini:/usr/local/etc/php/conf.d/dev.ini
    depends_on:
      - mysql
    networks:
      - wp-dev

  mysql:
    image: mysql:8.0
    container_name: wp-mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppass
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - wp-dev

  # Node.js container for build tools
  node:
    image: node:18-alpine
    container_name: wp-node
    working_dir: /app
    volumes:
      - ./themes/my-custom-theme:/app
    command: npm run watch
    networks:
      - wp-dev

  # Mailhog for email testing
  mailhog:
    image: mailhog/mailhog
    container_name: wp-mail
    ports:
      - "1025:1025"
      - "8025:8025"
    networks:
      - wp-dev

volumes:
  mysql-data:
  wp-uploads:

networks:
  wp-dev:
    driver: bridge

Plugin Development Workflow

Efficient plugin development with Docker:

Plugin File Structure

<?php
/**
 * Plugin Name: My Custom Plugin
 * Plugin URI: https://example.com/
 * Description: A custom WordPress plugin
 * Version: 1.0.0
 * Author: Your Name
 * Text Domain: my-custom-plugin
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

// Define plugin constants
define('MCP_VERSION', '1.0.0');
define('MCP_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('MCP_PLUGIN_URL', plugin_dir_url(__FILE__));

// Include required files
require_once MCP_PLUGIN_DIR . 'includes/class-plugin.php';

// Initialize plugin
function mcp_init() {
    $plugin = new My_Custom_Plugin();
    $plugin->run();
}
add_action('plugins_loaded', 'mcp_init');

Development Tools Integration

{
  "name": "my-custom-theme",
  "version": "1.0.0",
  "scripts": {
    "dev": "webpack --mode development --watch",
    "build": "webpack --mode production",
    "watch": "browser-sync start --proxy 'localhost:8080' --files '**/*.php, **/*.css, **/*.js'",
    "lint": "eslint assets/js/**/*.js",
    "format": "prettier --write '**/*.{js,css,scss}'"
  },
  "devDependencies": {
    "webpack": "^5.88.0",
    "webpack-cli": "^5.1.4",
    "browser-sync": "^2.29.3",
    "sass": "^1.63.6",
    "autoprefixer": "^10.4.14"
  }
}

Managing Environment Variables

Using different configurations for development, staging, and production:

graph LR A[.env.development] --> D[docker-compose.yml] B[.env.staging] --> D C[.env.production] --> D D --> E[WordPress Container] style A fill:#4caf50 style B fill:#ff9800 style C fill:#f44336

Environment-Specific Files

# .env.development
WP_ENVIRONMENT_TYPE=local
WP_DEBUG=true
WP_DEBUG_LOG=true
WP_DEBUG_DISPLAY=false
WP_CACHE=false
SCRIPT_DEBUG=true
DB_HOST=mysql
DB_NAME=wordpress_dev
DB_USER=wp_dev
DB_PASSWORD=devpass123

# .env.staging
WP_ENVIRONMENT_TYPE=staging
WP_DEBUG=true
WP_DEBUG_LOG=true
WP_DEBUG_DISPLAY=false
WP_CACHE=true
SCRIPT_DEBUG=false
DB_HOST=mysql
DB_NAME=wordpress_staging
DB_USER=wp_staging
DB_PASSWORD=stagingpass456

# .env.production
WP_ENVIRONMENT_TYPE=production
WP_DEBUG=false
WP_DEBUG_LOG=false
WP_DEBUG_DISPLAY=false
WP_CACHE=true
SCRIPT_DEBUG=false
DB_HOST=mysql
DB_NAME=wordpress_prod
DB_USER=wp_prod
DB_PASSWORD=prodpass789!

Loading Environment-Specific Configuration

# Development
docker-compose --env-file .env.development up -d

# Staging
docker-compose --env-file .env.staging up -d

# Production
docker-compose --env-file .env.production up -d

Multi-Site Development Environments

Running multiple WordPress sites simultaneously:

Multi-Project Docker Compose

# docker-compose.multi.yml
version: '3.8'

services:
  # Site 1: Client A
  wordpress-client-a:
    image: wordpress:latest
    container_name: wp-client-a
    ports:
      - "8081:80"
    environment:
      WORDPRESS_DB_HOST: mysql:3306
      WORDPRESS_DB_NAME: client_a_db
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass
    volumes:
      - ./sites/client-a/themes:/var/www/html/wp-content/themes
      - ./sites/client-a/plugins:/var/www/html/wp-content/plugins
    networks:
      - wp-multi-network

  # Site 2: Client B
  wordpress-client-b:
    image: wordpress:latest
    container_name: wp-client-b
    ports:
      - "8082:80"
    environment:
      WORDPRESS_DB_HOST: mysql:3306
      WORDPRESS_DB_NAME: client_b_db
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass
    volumes:
      - ./sites/client-b/themes:/var/www/html/wp-content/themes
      - ./sites/client-b/plugins:/var/www/html/wp-content/plugins
    networks:
      - wp-multi-network

  # Shared MySQL for all sites
  mysql:
    image: mysql:8.0
    container_name: wp-multi-mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_MULTIPLE_DATABASES: client_a_db,client_b_db
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppass
    volumes:
      - mysql-multi-data:/var/lib/mysql
      - ./config/mysql/init-multi-db.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - wp-multi-network

  # Reverse proxy for nice URLs
  nginx-proxy:
    image: nginx:alpine
    container_name: wp-proxy
    ports:
      - "80:80"
    volumes:
      - ./config/nginx/proxy.conf:/etc/nginx/nginx.conf
    depends_on:
      - wordpress-client-a
      - wordpress-client-b
    networks:
      - wp-multi-network

volumes:
  mysql-multi-data:

networks:
  wp-multi-network:
    driver: bridge

Workflow Automation Scripts

Useful scripts to streamline your Docker WordPress workflow:

Makefile for Common Tasks

# Makefile
.PHONY: help up down restart logs shell backup restore clean

help:
	@echo "Available commands:"
	@echo "  make up        - Start all containers"
	@echo "  make down      - Stop all containers"
	@echo "  make restart   - Restart all containers"
	@echo "  make logs      - View container logs"
	@echo "  make shell     - Open WordPress container shell"
	@echo "  make backup    - Backup database and files"
	@echo "  make restore   - Restore from backup"
	@echo "  make clean     - Remove containers and volumes"

up:
	docker-compose up -d
	@echo "WordPress is running at http://localhost:8080"

down:
	docker-compose down

restart:
	docker-compose restart

logs:
	docker-compose logs -f

shell:
	docker exec -it wp-dev bash

wp-cli:
	docker exec -it wp-dev wp $(filter-out $@,$(MAKECMDGOALS))

backup:
	@echo "Creating backup..."
	@mkdir -p backups
	@docker exec wp-mysql mysqldump -u root -prootpass wordpress > backups/db-$$(date +%Y%m%d-%H%M%S).sql
	@docker run --rm -v wordpress-docker_wp-uploads:/data -v $$(pwd)/backups:/backup alpine tar czf /backup/uploads-$$(date +%Y%m%d-%H%M%S).tar.gz -C /data .
	@echo "Backup completed!"

restore:
	@echo "Restoring from backup..."
	@docker exec -i wp-mysql mysql -u root -prootpass wordpress < $(filter-out $@,$(MAKECMDGOALS))
	@echo "Restore completed!"

clean:
	docker-compose down -v
	@echo "All containers and volumes removed!"

# Catch all target for wp-cli
%:
	@:

Docker Workflow Best Practices

  • Use bind mounts for development:Mount your theme/plugin directories for instant changes
  • Separate environments:Use different .env files for dev, staging, and production
  • Version control strategy:Commit docker-compose.yml but not .env files
  • Resource limits:Set memory and CPU limits to prevent resource exhaustion
  • Health checks:Implement health checks for all services
  • Log management:Configure log rotation and centralized logging

Real World Example: Complete Development Stack

A production-ready development environment with all tools:

# docker-compose.dev.yml
version: '3.8'

services:
  wordpress:
    image: wordpress:6.4-php8.2-apache
    container_name: wp-dev
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: mysql:3306
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass
      WORDPRESS_DEBUG: 'true'
      WORDPRESS_CONFIG_EXTRA: |
        define('WP_ENVIRONMENT_TYPE', 'local');
        define('SCRIPT_DEBUG', true);
        define('WP_DISABLE_FATAL_ERROR_HANDLER', true);
        define('WP_MEMORY_LIMIT', '256M');
        define('WP_MAX_MEMORY_LIMIT', '512M');
    volumes:
      - ./themes:/var/www/html/wp-content/themes
      - ./plugins:/var/www/html/wp-content/plugins
      - wp-uploads:/var/www/html/wp-content/uploads
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - wp-dev

  mysql:
    image: mysql:8.0
    container_name: wp-mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppass
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - wp-dev

  redis:
    image: redis:alpine
    container_name: wp-redis
    ports:
      - "6379:6379"
    networks:
      - wp-dev

  elasticsearch:
    image: elasticsearch:7.17.10
    container_name: wp-elasticsearch
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - "9200:9200"
    networks:
      - wp-dev

  adminer:
    image: adminer
    container_name: wp-adminer
    ports:
      - "8090:8080"
    networks:
      - wp-dev

volumes:
  mysql-data:
  wp-uploads:

networks:
  wp-dev:
    driver: bridge

Practice Exercise

Set up a complete development workflow:

💻
Try It Now
  1. Create a custom theme directory structure
  2. Set up docker-compose.yml with theme mounting
  3. Create environment files for dev and staging
  4. Implement the Makefile for automation
  5. Start the development environment:make up
  6. Make a change to your theme and see it live
  7. Use WP-CLI through Docker:make wp-cli plugin list
  8. Create a backup:make backup

Additional Resources