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 (wordpress
anddb
), connects them on a private network, maps ports (e.g.,8080
on your machine to80
in the container), and crucially, defines the volumes..devcontainer/Dockerfile
: A recipe for a custom WordPress image. It starts with an officialwordpress
image 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.yml
file, 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-content
folder 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-content
folder 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/uploads
folder.
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:latest
and 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-content
folderβ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-content
folder. 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-project
directory. - Reopen in Container: As soon as you open the folder, VS Code’s Dev Containers extension will detect the
.devcontainer
folder 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+P
orCmd+Shift+P
and 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
.env
file:- 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* the
wordpress` 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
.env
file, changeXDEBUG_MODE=off
toXDEBUG_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.json
file. 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!