Marcio Cunha

WordPress with Docker: How to Build a Local Development Environment

Learn how to build an isolated, reproducible local WordPress environment using Docker and Docker Compose, eliminating dependency conflicts and speeding up your web projects.

Marcio Cunha12 min
Also available in:EspañolPortuguês
Summary
  • Isolated container environments prevent version conflicts among different local projects.
  • Docker Compose simplifies the orchestration of multiple services like PHP, Nginx, and MySQL.
  • Persistent volumes ensure theme files and databases are not lost when restarting containers.
  • Internal virtual networks allow secure and fast communication between WordPress and the database.
  • Centralized environment variables make it easy to change credentials without modifying source code.

Why Abandon Traditional Local Installations

For a long time, setting up WordPress on your own machine meant installing complete software stacks directly onto the operating system, such as the famous XAMPP or MAMP. In practice, this meant that your main machine's PHP libraries and versions were tied to a single project. If a new site required a newer PHP version and the old site broke due to incompatibility, the developer entered a maze of manual configurations. Docker solves this problem by encapsulating the application and all its dependencies inside containers, which function as closed boxes independent of the rest of the operating system.

When we talk about modern web development, reproducibility is the key to mental sanity. A Docker container ensures that your code will run in the exact same way on your laptop, on the staging server, and on your teammate's machine. In the context of WordPress, this means that PHP, the web server, and the MySQL database run in isolated environments, yet are perfectly integrated via local virtual networks. The productivity boost is immediate, eliminating the classic 'it works on my machine' phrase from your daily routine.

Understanding the Essential Components of a WordPress Stack

For WordPress to run autonomously, we need to bring together three fundamental software pieces, technically known as a LEMP or LAMP stack. In practice, the first component is the web server, like Nginx or Apache, responsible for receiving browser requests and delivering pages. The second component is the PHP interpreter, which processes WordPress file logic and builds pages dynamically. The third component is the relational database, usually MySQL or MariaDB, where posts, comments, users, and site settings are stored.

In the Docker ecosystem, each of these pieces typically lives in a separate, specialized container. Instead of stuffing everything inside a single giant container — which would violate microservices and isolation best practices — we divide responsibilities. The PHP container handles code processing exclusively, while the MySQL container handles structured data storage alone. This modularity allows you to upgrade the PHP version without touching the database, or restart the web server without losing active connections.

Configuring the Orchestrator with Docker Compose

Managing multiple containers manually via long terminal commands would be exhausting and error-prone. This is where Docker Compose comes in, a tool that lets you define and run multi-container Docker applications through a single configuration file in YAML format. In practice, this file acts like a blueprint for a house, where you sketch out exactly which rooms (containers) will exist, what ports they will use to communicate with the outside world, and how they will connect to each other.

Below is a practical example of a docker-compose.yml file configured to spin up a robust WordPress environment alongside a dedicated MySQL database:

version: '3.8'

services:
  db:
    image: mysql:8.0
    container_name: wp_db
    restart: always
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wp_user
      MYSQL_PASSWORD: secret_password
      MYSQL_ROOT_PASSWORD: root_password
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - wp_network

  wordpress:
    image: wordpress:latest
    container_name: wp_app
    restart: always
    ports:
      - '8080:80'
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wp_user
      WORDPRESS_DB_PASSWORD: secret_password
    volumes:
      - ./wp-content:/var/www/html/wp-content
    networks:
      - wp_network

volumes:
  db_data:

networks:
  wp_network:
    driver: bridge

This YAML file establishes a solid bridge between the application and the database. The service named db uses the official MySQL image, setting secure credentials and creating a persistent volume called db_data so database information does not vanish when we turn off our computer. Meanwhile, the wordpress service maps port 8080 on your computer to port 80 of the container, letting you access the site in your browser by typing http://localhost:8080.

Managing Data Persistence and Theme Files

One of the biggest fears for those starting with Docker is container volatility. By default, everything generated inside a container dies when it is destroyed or recreated. In practice, if the database were stored purely inside the MySQL container, deleting the container would mean losing all your WordPress posts and settings. To solve this, we use volumes and directory mounts, which connect folders from your physical computer directly inside the container.

In the setup we built, the line - ./wp-content:/var/www/html/wp-content creates a direct bridge between the wp-content folder of your local project and the corresponding folder inside the WordPress container. This means you can open your favorite code editor — like Visual Studio Code — on your physical machine, edit theme and plugin files, and see changes reflected instantly in the browser. The source code lives safely on your computer, while the Docker environment merely executes that code.

Troubleshooting Common Issues and Optimizing Workflow

Even with full automation, a few specific challenges may arise during local development with Docker. A classic problem involves file permissions on Linux and macOS, where the PHP container user might lack permission to write new theme files or uploads. In practice, this is usually solved by adjusting wp-content folder permissions or properly configuring user mapping in the Compose file, ensuring you retain total control over files generated by the WordPress dashboard.

Another point of attention is I/O performance on shared file systems, especially in large projects with thousands of files in complex plugins like WooCommerce. Developers using macOS may experience slow page loading due to how the system translates file calls into the container. Utilizing optimized synchronization mechanisms or native Docker acceleration features helps keep navigation fluid and the environment agile for daily development.

Final Thoughts

Adopting Docker to manage local WordPress environments radically transforms how we approach web development. By encapsulating dependencies and standardizing infrastructure, we eliminate wasted hours installing packages and debugging obscure operating system incompatibilities. The initial learning investment in Docker Compose syntax pays off quickly through stability, portability, and peace of mind to focus on what truly matters: writing quality code.

With the container structure properly configured, you gain the freedom to switch between multiple projects simultaneously, test different PHP versions with a single command, and share the complete environment transparently with your team. Try applying this architecture to your next project and feel the difference of working with a truly modern, predictable, and professional development workflow.