Tired of the “it works on my machine” headache? Sick of clunky local servers like MAMP or XAMPP that never quite match your live environment? It’s time to modernize your WordPress development workflow. This guide will walk you through creating a powerful, consistent, and isolated development environment using Docker, VS Code, and Dev Containers.
Once you’re done, you’ll have a professional setup that’s easy to version control, share with teammates, and spin up in minutes. Let’s dive in! π
Quick Review & Key Points
Quick Review for Interviews
This setup creates a professional, containerized WordPress development environment using Docker and VS Code. The primary goal is to solve “it works on my machine” problems by ensuring every developer has an identical, isolated workspace.
- Core Technologies:
- Docker: Used to create and manage isolated environments called containers for the WordPress site and its database.
- VS Code + Dev Containers Extension: Integrates the code editor directly into the running Docker container for a seamless development experience.
- Key Configuration Files:
docker-compose.yml: The main blueprint. It defines the two services (wordpressanddb), connects them on a private network, maps ports (e.g.,8080on your machine to80in the container), and crucially, defines the volumes..devcontainer/Dockerfile: A recipe for a custom WordPress image. It starts with an officialwordpressimage and adds essential developer tools like WP-CLI, Xdebug (for debugging), and Node.js/npm..devcontainer/devcontainer.json: The configuration file for VS Code. It tells the editor how to use thedocker-compose.ymlfile, which service to connect to (wordpress), what extensions to automatically install inside the container, and what commands to run after creation (like fixing file permissions)..env: A simple text file for storing secrets like database passwords. It’s kept out of version control to maintain security.
- The Most Important Concept: Volumes and Data Persistence
- The setup uses a bind mount to link your local
./wp-contentfolder directly into the container at/var/www/html/wp-content. - Your code (themes, plugins) lives on your computer’s filesystem, not inside the container. The container only gets a “view” of these files.
- This is critical for safety: if you rebuild the container (e.g., to update PHP or install a new tool), the old container is destroyed, but your
wp-contentfolder remains completely safe and untouched on your machine. The new container simply links to it again. This prevents any loss of work. - The database uses a named volume (
db_data) for the same reason, ensuring your posts and settings are preserved separately from the database container’s lifecycle.
- The setup uses a bind mount to link your local
Prerequisites: What You’ll Need
Before we start building, make sure you have the following software installed on your system:
- Docker Desktop: This is the engine that runs our containers. You can download it for Windows, Mac, or Linux.
- Visual Studio Code (VS Code): Our code editor of choice. If you don’t have it, grab it here.
- Dev Containers Extension: This is the magic that connects VS Code to our Docker containers. Install it directly from the VS Code Marketplace.
A Note for Windows Users: Setting up WSL & Bash
For the best experience on Windows, Docker Desktop uses the Windows Subsystem for Linux (WSL). WSL lets you run a genuine Linux environment directly on Windows, without the overhead of a traditional virtual machine. This is how you’ll get access to a bash terminal.
Setting it up is a breeze.
- Open PowerShell or Command Prompt as an Administrator.
- Run the following command. This will enable all necessary features and install the default Ubuntu distribution of Linux.
PowerShell / Command Prompt:
wsl --install
After a restart, you’re all set! You can access your Linux terminal by simply typing wsl or bash in your command prompt or by launching the “Ubuntu” app from your Start Menu.
Project Structure: The Blueprint
A well-organized project is a happy project. Here’s the file and folder structure we’ll be creating. This layout keeps our configuration clean and separates our custom code from the WordPress core.
your-wordpress-project/ βββ .devcontainer/ β βββ Dockerfile β βββ devcontainer.json βββ wp-content/ β βββ plugins/ β βββ themes/ βββ .env βββ docker-compose.yml
your-wordpress-project/: The root of your entire project..devcontainer/: This special folder holds the configuration for our VS Code development container.wp-content/: This is where you live! Your custom themes and plugins go here. We’ll mount this directory directly into our WordPress container..env: A simple text file to store our environment variables, like database passwords. It’s crucial to keep secrets out of your main configuration files.docker-compose.yml: The master plan. This file tells Docker how to build and connect our services (WordPress and a database).
The Configuration Files: A Deep Dive
Let’s break down each configuration file. Copy and paste the following code into the corresponding files within your project structure.
1. The Environment File (.env)
First, create a file named .env in your project root. This file will hold all our secrets and environment-specific settings. Remember to add .env to your .gitignore file to avoid committing secrets to version control!
# .env # Database Credentials MYSQL_ROOT_PASSWORD=supersecretrootpassword MYSQL_DATABASE=wordpress_dev MYSQL_USER=wp_user MYSQL_PASSWORD=wp_password # Xdebug Configuration # Set XDEBUG_MODE to 'debug' to enable debugging. Leave as 'off' otherwise. XDEBUG_MODE=off XDEBUG_PORT=9003
These variables are automatically read by Docker Compose and injected into our containers.
2. The Docker Compose File (docker-compose.yml)
This file is the orchestra conductor. It defines the services (containers) that make up our application and how they interact.
# docker-compose.yml
version: '3.8'
services:
# WordPress Service (where VS Code will connect)
wordpress:
build:
context: . # Build context is the project root
dockerfile: .devcontainer/Dockerfile # Path to your Dockerfile
# IMPORTANT: Change this to a unique name for your project!
container_name: your-project-name-wordpress
restart: unless-stopped
volumes:
# Mount your local wp-content into the container's wp-content
- ./wp-content:/var/www/html/wp-content:cached
ports:
- "8080:80" # Map port 8080 on host to 80 in container
environment:
WORDPRESS_DB_HOST: db:3306 # Service name 'db' and default port
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
WORDPRESS_DB_USER: ${MYSQL_USER}
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
# Add Xdebug configuration environment variables
XDEBUG_MODE: ${XDEBUG_MODE:-off}
XDEBUG_CONFIG: "client_host=host.docker.internal client_port=${XDEBUG_PORT:-9003}"
depends_on:
- db # Wait for the database to be ready
networks:
- wp_dev_network
# Database Service (MariaDB - MySQL compatible)
db:
image: mariadb:10.6 # Use a specific version for stability
# IMPORTANT: Change this to a unique name for your project!
container_name: your-project-name-db
restart: unless-stopped
volumes:
- db_data:/var/lib/mysql # Persist database data using a named volume
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
networks:
- wp_dev_network
# Named volume for persistent database storage
volumes:
db_data:
# Custom network for services to communicate
networks:
wp_dev_network:
driver: bridge
The Magic of Volumes: Keeping Your Code Safe
The single most important concept for development in this setup is the volume mount. Look at this line in the wordpress service:
volumes: - ./wp-content:/var/www/html/wp-content:cached
This line does not copy your files. Instead, it creates a live, two-way sync or “mirror” between the ./wp-content folder on your local computer (the host) and the /var/www/html/wp-content folder inside the running container.
- When you edit a theme file in VS Code on your machine, that change is instantly available inside the container because it’s the exact same file.
- When WordPress uploads a media file inside the container, that file instantly appears in your local
./wp-content/uploadsfolder.
How Volumes Protect Your Files
Containers are designed to be ephemeral, meaning they can be thrown away and replaced at any time. This is a good thing! It allows you to quickly update your environment or start fresh. But what happens to your code?
- Without a volume: Any file created inside a container’s filesystem is part of that container. If you rebuild the container (for instance, because you used
image: wordpress:latestand a new version was released), the old container is deleted, and all the code you wrote inside it is lost forever. - With a volume: Your
wp-contentfolderβthe home of your themes and pluginsβlives safely on your local filesystem. It is not part of the container. When you rebuild, Docker throws away the old container and creates a brand new one. This new container then simply “mounts” or connects to your existing localwp-contentfolder. All your work remains untouched and is immediately available to the new container.
This separation of your persistent code (the volume) from the disposable application environment (the container) is the key to a safe and flexible development workflow. The same principle applies to the db_data volume for the database, which ensures your posts, pages, and settings survive even if the database container is rebuilt.
3. The Dockerfile (.devcontainer/Dockerfile)
The Dockerfile contains the build instructions for our custom WordPress container. It starts with an official WordPress image and then adds our development tools on top.
# .devcontainer/Dockerfile
# Start from an official WordPress image
FROM wordpress:6.5-php8.2-apache
# Switch to root user to install packages
USER root
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
zip unzip \
vim \
sudo \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
nodejs \
npm \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# --- Install and Configure Xdebug ---
RUN pecl install xdebug && docker-php-ext-enable xdebug
RUN echo "xdebug.mode = \${XDEBUG_MODE:-off}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
echo "xdebug.start_with_request = yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
echo "xdebug.client_host = host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
echo "xdebug.client_port = \${XDEBUG_PORT:-9003}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
echo "xdebug.log = /tmp/xdebug.log" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
echo "xdebug.idekey = VSCODE" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
# --- Install common PHP extensions ---
RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
docker-php-ext-install -j$(nproc) gd zip mysqli opcache
# --- Install WP-CLI ---
RUN curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
chmod +x wp-cli.phar && \
mv wp-cli.phar /usr/local/bin/wp
# --- Permissions ---
RUN chown -R www-data:www-data /var/www/html/wp-content
# Switch back to the default Apache user for better security
USER www-data
4. The Dev Container JSON (.devcontainer/devcontainer.json)
This file is purely for VS Code. It tells the Dev Containers extension how to integrate our Docker setup into the editor, creating a seamless experience.
// .devcontainer/devcontainer.json
{
"name": "WordPress Dev Environment (Docker)",
"dockerComposeFile": [
"../docker-compose.yml"
],
"service": "wordpress",
"workspaceFolder": "/var/www/html",
"postCreateCommand": "sudo chown -R www-data:www-data /var/www/html/wp-content && echo 'Container ready!'",
"customizations": {
"vscode": {
"settings": {
"intelephense.environment.phpVersion": "8.2.0",
"php.validate.executablePath": "/usr/local/bin/php",
"[php]": {
"editor.defaultFormatter": "bmewburn.vscode-intelephense-client",
"editor.formatOnSave": true
}
},
"extensions": [
"bmewburn.vscode-intelephense-client",
"xdebug.php-debug",
"eamodio.gitlens",
"streetsidesoftware.code-spell-checker",
"esbenp.prettier-vscode",
"EditorConfig.EditorConfig"
]
}
},
"remoteUser": "root"
}
Let’s Build It! Launching Your Environment
With all the files in place, the magic is about to happen.
- Open the Project in VS Code: Launch VS Code and go to
File > Open Folder...and select youryour-wordpress-projectdirectory. - Reopen in Container: As soon as you open the folder, VS Code’s Dev Containers extension will detect the
.devcontainerfolder and a notification will pop up in the bottom-right corner. Click “Reopen in Container”. (If you miss the pop-up, open the Command Palette withCtrl+Shift+PorCmd+Shift+Pand search for “Dev Containers: Reopen in Container”.) - Wait for the Build: The first time you do this, Docker will need to build your custom image. This might take a few minutes, but subsequent launches will be much faster.
- Welcome to Your Dev Site! Once the build is complete, your VS Code window will reload. Open your web browser and navigate to
http://localhost:8080. - Install WordPress: You’ll be greeted by the famous WordPress installation screen. When it asks for database credentials, use the exact same values you put in your
.envfile:- Database Name:
wordpress_dev - Username:
wp_user - Password:
wp_password - Database Host:
db(This is crucial! Don’t uselocalhost.)
- Database Name:
That’s it! You now have a fully functional, local WordPress site running in Docker.
Using Your New Superpowers
Your environment is ready. Now what?
Seamless Theme and Plugin Development
Go to the wp-content/themes folder on your local machine. Create a new theme or modify an existing one. Save the file. Because our wp-content directory is mounted as a volume, the changes are instantly reflected inside the container. Just refresh your browser to see them. No FTP, no file syncingβjust pure, simple development.
Using WP-CLI
Open the terminal inside VS Code (Ctrl+\`` or Cmd+`). You are now in a bashshell *inside* thewordpress` container. You have full access to WP-CLI. Try it out:
Bash (inside the VS Code terminal):
# List installed plugins wp plugin list # Install and activate a plugin wp plugin install query-monitor --activate # Check the WordPress version wp core version
Debugging with Xdebug
This is the real game-changer. Let’s set up step-debugging.
- Enable Xdebug: In your
.envfile, changeXDEBUG_MODE=offtoXDEBUG_MODE=debug. - Rebuild the Container: Open the Command Palette (
Ctrl+Shift+P) and run “Dev Containers: Rebuild Container”. - Create a Launch Configuration: In VS Code, go to the “Run and Debug” panel (the bug icon on the sidebar). Click “create a launch.json file” and select “PHP”. This will create a
.vscode/launch.jsonfile. Replace its contents with this:
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/var/www/html/": "${workspaceFolder}"
}
}
]
}
- Start Debugging: Set a breakpoint (a red dot) next to a line number in a PHP file. In the “Run and Debug” panel, press the green “Play” button next to “Listen for Xdebug.” Refresh your WordPress site, and VS Code will pause execution at your breakpoint.
Conclusion
You’ve successfully built a modern, robust WordPress development environment that will serve you well on projects of any size. By defining your entire stack in code and separating your persistent data with volumes, you’ve created a portable, reproducible, and safe workspace that will save you countless hours and headaches.
Welcome to the future of WordPress development. Happy coding!