SOLARISE
DEV

Setting up a Reliable Local WordPress Workflow on Windows with Docker

Dealing with local WordPress development can be a challenge but Docker packages it all up neatly

Developing WordPress sites locally on Windows has traditionally involved tools like XAMPP or WAMP. While they get the job done, they can sometimes be clunky, lead to the dreaded "it works on my machine!" syndrome, or create conflicts when juggling multiple projects with different server requirements. It's quite useful, actually, to find a better way. If you're a Windows developer looking for a more robust, consistent, and modern local development setup for WordPress, it's time to embrace Docker.

This guide will walk you through setting up WordPress locally on your Windows machine using Docker Desktop, covering everything from initial setup to advanced configurations and troubleshooting common issues. We'll make your development environment isolated, portable, and much closer to a production setup.

Why Docker for WordPress on Windows?

Docker "containerizes" applications, meaning it packages an application and all its dependencies (like PHP, MySQL, Apache/Nginx) into a standardized unit. For WordPress development on Windows, this offers several key advantages:

  • Environment Consistency: Docker containers ensure your local environment closely mirrors your live server, reducing surprises during deployment.
  • Isolation: Each project runs in its own isolated containers. No more conflicting PHP or MySQL versions between projects!
  • Easy Multi-Project Management: Effortlessly run multiple WordPress sites, each with its specific PHP or database version, without them interfering with each other.
  • Portability: Your Docker setup, defined in a docker-compose.yml file, can be easily shared with team members or used across different machines.
  • Cleanliness: Installs and uninstalls are clean. No leftover services or configuration files cluttering your Windows system.

Getting Started: What You'll Need

  • Docker Desktop for Windows: This is the official Docker application for Windows. It integrates seamlessly with Windows and uses WSL 2 (Windows Subsystem for Linux 2) for running Linux containers, which is ideal for WordPress.
    • Download from: Docker Desktop
    • Ensure WSL 2 is enabled and set as the default backend in Docker Desktop settings.
  • A Text Editor: Something like VS Code, Sublime Text, or PhpStorm.
  • Basic Command Line Familiarity: You'll be using PowerShell or your preferred terminal to run Docker commands.

Step-by-Step Guide to Your Dockerized WordPress Setup

Let's build your local WordPress environment piece by piece.

Step 1: Basic Docker Compose Setup

Docker Compose is a tool for defining and running multi-container Docker applications. We'll use a docker-compose.yml file to define our WordPress site and its database.

1. Create a Project Directory:

Create a new folder for your WordPress project anywhere on your system (e.g., C:\Users\YourUser\Projects\MyWordPressSite).

2. Create docker-compose.yml:

Inside your project directory, create a file named docker-compose.yml and add the following:


version: '3.8'

services:
  db:
    image: mariadb:10.11 # Or your preferred MariaDB/MySQL version
    container_name: my_wordpress_db
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: your_strong_root_password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress_user
      MYSQL_PASSWORD: your_strong_user_password
    networks:
      - wp_network

  wordpress:
    image: wordpress:latest # Official WordPress image
    container_name: my_wordpress_app
    volumes:
      - wp_data:/var/www/html # For WordPress core, themes, plugins
    ports:
      - "80:80" # Maps host port 80 to container port 80
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306 # Service name 'db' and its internal port
      WORDPRESS_DB_USER: wordpress_user
      WORDPRESS_DB_PASSWORD: your_strong_user_password
      WORDPRESS_DB_NAME: wordpress
    depends_on:
      - db
    networks:
      - wp_network

volumes:
  db_data:
  wp_data:

networks:
  wp_network:
    driver: bridge

Explanation of docker-compose.yml:

  • services: Defines the containers.
    • db: Our MariaDB database.
      • image: Specifies the MariaDB version.
      • volumes: db_data:/var/lib/mysql creates a persistent named volume db_data to store your database files. This ensures your data isn't lost when the container stops.
      • environment: Sets up database credentials. Change these passwords!
    • wordpress: Our WordPress application.
      • image: Uses the latest official WordPress image.
      • volumes: wp_data:/var/www/html creates a persistent named volume wp_data. Initially, this is where WordPress files live. We'll change this later for easier development.
      • ports: - "80:80" maps port 80 on your Windows machine (host) to port 80 inside the WordPress container.
      • environment: Configures WordPress to connect to the db service.
      • depends_on: Ensures the db service starts before the wordpress service.
  • volumes: Declares the named volumes for data persistence.
  • networks: Creates a custom bridge network wp_network for our services to communicate.

3. Start Your Engines!

Open your terminal (PowerShell, Command Prompt, or Windows Terminal) in your project directory and run:

docker compose up -d

The -d flag runs the containers in detached mode (in the background). Docker will download the images (if you don't have them already) and start the containers.

4. WordPress Installation:

Open your web browser and go to http://localhost. You should see the WordPress "famous five-minute installation" screen. Complete the setup.


Step 2: Managing Multiple Projects with Different Ports

If you want to run multiple WordPress sites (or other web projects) simultaneously, they can't all use port 80 on your host machine. You need to map different host ports to each project's container port 80.

In your docker-compose.yml for the wordpress service, change the ports section:

  • For Project 1 (already done, using port 80):
    ports:\n  - "80:80" # Access at http://localhost
  • For Project 2 (in a new project folder with its own docker-compose.yml):
    ports:\n  - "8080:80" # Access at http://localhost:8080
  • For Project 3:
    ports:\n  - "8001:80" # Access at http://localhost:8001

And so on. Just pick a host port that isn't already in use.


Step 3: Easy Database Access with phpMyAdmin

phpMyAdmin is a popular web interface for managing MySQL/MariaDB databases. Let's add it to our setup.

Edit your docker-compose.yml and add a new service for phpmyadmin:


services:
  db:
    # ... (your existing db configuration) ...

  wordpress:
    # ... (your existing wordpress configuration) ...

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    container_name: my_wordpress_phpmyadmin
    restart: always
    ports:
      - "8008:80" # Access phpMyAdmin on host port 8008 (choose an available port)
    environment:
      PMA_HOST: db         # Tells phpMyAdmin the hostname of the database service ('db')
      PMA_PORT: 3306       # The internal port of the database service
      # MYSQL_ROOT_PASSWORD: your_strong_root_password # Optional: if you want to login as root
    depends_on:
      - db
    networks:
      - wp_network
  • Make sure phpmyadmin is on the same wp_network.
  • Run docker compose up -d again to create and start the new phpMyAdmin container.
  • You can now access phpMyAdmin at http://localhost:8008 (or your chosen port).
  • For the server, enter db. Use the wordpress_user and your_strong_user_password (or root credentials) to log in.

Step 4: Seamless File System Access (Bind Mounts)

The named volume wp_data is good for persistence, but not ideal for direct theme/plugin development from your Windows file system. For that, we use bind mounts. A bind mount maps a directory on your host machine directly into the container.

1. Create a Local Folder for WordPress Files:

Inside your project directory (e.g., C:\Users\YourUser\Projects\MyWordPressSite), create a new folder named wordpress_files.

2. Modify docker-compose.yml:

Change the volumes section for your wordpress service:


services:
  wordpress:
    image: wordpress:latest
    container_name: my_wordpress_app
    volumes:
      # Replace the named volume with a bind mount:
      - ./wordpress_files:/var/www/html
    ports:
      - "80:80" # Or your chosen port
    # ... (rest of your wordpress service configuration)

The ./wordpress_files:/var/www/html line maps the wordpress_files folder (relative to your docker-compose.yml) to /var/www/html inside the container.

3. Restart and Populate:

  • If you had a previous wp_data volume, you might want to remove it to avoid conflicts or copy its contents to your new wordpress_files folder. To remove the old volume (this will delete its data!): docker volume rm MyWordPressSite_wp_data (the name might vary, check with docker volume ls).
  • Run docker compose up -d --force-recreate. The --force-recreate flag ensures the container is rebuilt with the new volume mapping.
  • When the WordPress container starts for the first time with an empty bind mount, it will copy the default WordPress files into your ./wordpress_files folder.

Now, any changes you make in C:\Users\YourUser\Projects\MyWordPressSite\wordpress_files will be instantly reflected in the container, making theme and plugin development a breeze!

Performance Note (WSL 2):

We'll cover this in troubleshooting, but for best performance, your project files (including wordpress_files) should ideally live inside your WSL 2 filesystem, not directly on the Windows C: drive.


Step 5: Sharing Your Local Site with ngrok (Temporarily)

Need to quickly show your local site to a client? ngrok creates a secure tunnel from a public URL to your local machine.

1. Download and Install ngrok:

  • Go to ngrok.com and download the version for Windows.
  • Unzip it and place ngrok.exe somewhere accessible (or add it to your PATH).

2. Connect Your Account (Optional but Recommended):

  • Sign up for a free account on ngrok.
  • Get your authtoken from the dashboard.
  • Run:
    ngrok authtoken YOUR_AUTHTOKEN

3. Expose Your WordPress Port:

If your WordPress site is running locally on http://localhost:80 (because of the 80:80 mapping in Docker Compose), run:

ngrok http 80

If you changed the host port (e.g., to 8080:80), use that port:

ngrok http 8080

ngrok will display a public URL (e.g., https://random-string.ngrok-free.app).

4. Update WordPress URLs (CRITICAL!):

  • WordPress stores its site URL in the database. To make your site accessible via the ngrok URL, you must temporarily update these settings.
  • Go to your WordPress admin dashboard (via http://localhost/wp-admin).
  • Navigate to Settings > General.
  • Change WordPress Address (URL) and Site Address (URL) to the https://<random-string>.ngrok-free.app URL provided by ngrok. Do not include a trailing slash.
  • Save changes. You might be logged out and need to log back in using the ngrok URL.

5. Share the ngrok URL with your client.

6. When Done:

  • Stop ngrok (Ctrl+C in its terminal).
  • Crucially, change the WordPress Address (URL) and Site Address (URL) back to http://localhost (or your local port) in the WordPress settings.

Troubleshooting Common Hurdles

You might encounter a few bumps along the road. Here's how to tackle them:

"Port is already allocated" (e.g., for port 80)

Problem: Another application on your Windows machine (like IIS, Skype, or another web server) is already using the host port you're trying to map.

Solution 1 (Find & Stop):

  1. Open PowerShell as Administrator.
  2. Run netstat -ano | findstr ":80" (replace :80 if it's a different port).
  3. Note the PID (Process ID) of the listening process.
  4. Open Task Manager, go to "Details", find the PID, and identify/stop the application or service. Common culprits are "World Wide Web Publishing Service" (IIS).

Solution 2 (Change Docker Port): Modify the host port in your docker-compose.yml for the wordpress service (e.g., ports: - "8081:80") and access your site at http://localhost:8081. Then run docker compose up -d --force-recreate.

"Read-only file system" or "Error creating temporary lease"

Problem: Docker is having trouble writing to its data directories or your volume mounts.

Solutions:

  • Restart Docker Desktop: Often the simplest fix.
  • Check Disk Space: Ensure your C: drive (and WSL 2 virtual disk) has enough free space.
  • Docker System Prune: Cleans up unused Docker resources. Run docker system prune -a. (Use docker system prune -a --volumes with extreme caution, as it removes unused named volumes).
  • WSL Restart: In PowerShell, run wsl --shutdown, wait a few seconds, then restart Docker Desktop.
"Exec format error" in container logs

Problem: Almost always a CPU architecture mismatch (e.g., trying to run an amd64 image on an arm64 machine like Apple Silicon or Windows on ARM, or vice-versa, without proper emulation or the correct image variant).

Solution:

  1. Stop containers: docker compose down
  2. Remove the problematic local images (e.g., docker rmi wordpress:latest mariadb:10.11 phpmyadmin:latest).
  3. Re-pull images: docker compose pull (Docker should pick the correct architecture).
  4. Restart containers: docker compose up -d --force-recreate.
Slow `localhost` Access (The WSL 2 Filesystem Bottleneck)

Problem: If your project files (the bind mount like ./wordpress_files) are on your Windows filesystem (e.g., C: drive), accessing them from the WordPress container (running in WSL 2) is slow due to the filesystem boundary crossing. WordPress reads many PHP files on each request, leading to sluggish performance.

Solution (Highly Recommended for Performance): Store project files inside the WSL 2 filesystem.

  1. Open WSL Terminal: Type wsl in PowerShell or Command Prompt.
  2. Create Project Directory in WSL: mkdir -p ~/projects/my_wp_site and cd ~/projects/my_wp_site.
  3. Move/Copy Files: Copy your wordpress_files from Windows (e.g., /mnt/c/Users/YourUser/Projects/MyWordPressSite/wordpress_files) into this WSL directory.
    # Inside WSL terminal
    cp -r /mnt/c/Users/YourUser/Projects/MyWordPressSite/wordpress_files ./
  4. Update docker-compose.yml:
    • Best: Move your docker-compose.yml into the WSL project directory (e.g., ~/projects/my_wp_site/docker-compose.yml).
    • Change the volume mapping to a relative WSL path:
      volumes:\n  - ./wordpress_files:/var/www/html
    • Or, if docker-compose.yml stays on Windows, use the //wsl$/DISTRO_NAME/path/to/files format (e.g., //wsl$/Ubuntu/home/your_wsl_user/projects/my_wp_site/wordpress_files:/var/www/html).
  5. Develop Inside WSL: Use VS Code with the "Remote - WSL" extension to open your project folder directly within WSL. This gives native file access speed.
  6. Run docker compose down and docker compose up -d --force-recreate from the directory containing your (potentially updated) docker-compose.yml.
phpMyAdmin Not Working / Keeps Restarting
  • Check Logs: docker logs my_wordpress_phpmyadmin (or your container name).
  • Verify PMA_HOST: Ensure it's set to db (or your database service name) in docker-compose.yml.
  • Database Healthy: Check logs of your db container (docker logs my_wordpress_db). phpMyAdmin can't connect if the database itself isn't running correctly.
Increasing phpMyAdmin Import Size

Solution: Add the UPLOAD_LIMIT environment variable to your phpmyadmin service in docker-compose.yml:


environment:
  PMA_HOST: db
  PMA_PORT: 3306
  UPLOAD_LIMIT: 256M # Or your desired size (e.g., 1G)
        

Then run docker compose up -d --force-recreate phpmyadmin.


A Quick Look at Alternatives

While Docker offers immense flexibility and consistency, it's worth knowing other popular local development tools for WordPress on Windows:

  • XAMPP/WAMP/MAMP: These are traditional all-in-one packages that install Apache, MySQL, and PHP directly onto your Windows system.
    • Pros: Can be simpler for absolute beginners to get a single site running quickly.
    • Cons: Managing different PHP/MySQL versions for multiple projects is difficult, can lead to system clutter, and environments might not match production closely.
    • XAMPP: https://www.apachefriends.org/
  • Local (formerly Local by Flywheel/LocalWP): A fantastic GUI-based tool specifically designed for local WordPress development.
    • Pros: Extremely easy to spin up new WordPress sites, change PHP/MySQL versions per site, create "Blueprints", and offers tools like MailHog and direct deployment to WP Engine/Flywheel.
    • Cons: More WordPress-centric (less ideal if you also develop non-WordPress PHP apps). It still manages underlying services, which can sometimes have their own quirks, though generally it's very robust.
    • Local: https://localwp.com/

Docker sits as a more powerful, developer-focused solution that gives you fine-grained control over your entire stack, making it an industry standard for many development workflows beyond just WordPress.


Conclusion: Embrace the Dockerized WordPress Workflow!

Moving your local WordPress development on Windows to Docker might seem like a bit of a learning curve initially, but the benefits in terms of consistency, isolation, flexibility, and cleaner project management are well worth it. You'll spend less time fighting environment issues and more time building awesome WordPress sites.

This guide has covered the essentials to get you up and running, along with solutions to common pain points. Happy Dockerizing!

Robin Metcalfe

Robin is a freelance web strategist and developer based in Edinburgh, with over 15 years of experience helping businesses build effective and engaging online platforms using technologies like Laravel and WordPress.

Get in Touch