Docker for WordPress development in 2026: Containerization made easy

Docker for WordPress development in 2026: Containerization made easy

Last verified: October 14, 2026
15 min read
Guide
Full-stack developer

If you are a professional WordPress developer in 2026, Docker is your best friend. Gone are the days of manually installing PHP versions on your Mac or fighting with Windows-specific server bugs. Docker has become the common language of development environments, and knowing it well is part of the job for anyone working in the WordPress ecosystem today.

This guide covers Docker applied to WordPress development: from the basics and the official wordpress image to Compose, wp-env, debugging, CI/CD and migrating away from MAMP or LocalWP. The technical details come from the official Docker documentation, the wordpress image page on Docker Hub, MariaDB and the WordPress developer handbook, linked in each section.

#Why use Docker for WordPress development

The “works on my machine” excuse stops working once the environment lives in versioned files. Containerization ensures consistency between environments, and that shift has changed how WordPress teams collaborate and ship software.

#Parity between local and production environments

Your local container can use the same Linux distribution, PHP version, and MySQL configuration as your production server. That cuts down the most frustrating category of bugs: the ones that only show up in production. When you develop inside a Docker container built from the same image the server runs, code that works on your laptop behaves the same on the staging server and in production.

Environment parity goes beyond software versions. It covers network configuration, environment variables and file permissions. With Docker, every team member works in an identical environment, whether the host operating system is macOS, Windows or Linux.

#Several PHP versions in isolated projects

Project A needs PHP 8.4, and Project B needs PHP 7.4? No problem. Each project lives in its own “bubble” and never conflicts with others. This level of isolation was hard to get with traditional tools like MAMP or XAMPP, where changing the PHP version affected every project at once.

Docker isolation also protects your host operating system. PHP extensions, system libraries and project dependencies stay separate. If one project needs an experimental PHP extension or an unusual server configuration, you can add it without risk to your other projects.

#A reproducible environment from files versioned in Git

Every Docker environment is defined by configuration files versioned in Git. A new team member clones the repository, runs one command and has the environment working, with no step-by-step setup guide that slowly goes out of date. The environment documentation lives in the project code itself.

#WordPress Docker Compose setup

Docker Compose is the tool that coordinates multiple containers. A typical 2026 WordPress setup includes several services that work together as a complete development environment.

#Which services WordPress needs in Docker Compose

  1. WordPress (PHP-FPM or Apache): the engine that runs the WordPress PHP code.
  2. Nginx: the web server in front of PHP-FPM, if you choose the fpm variant.
  3. MariaDB/MySQL: the database that stores all the content.
  4. Redis: the object cache, worth adding if the project uses it in production.
  5. Mailpit: to catch outgoing emails during testing.
# compose.yaml snippet with the FPM variant
services:
  db:
    image: mariadb:11.4
    volumes:
      - db_data:/var/lib/mysql
  wordpress:
    image: wordpress:php8.4-fpm
    depends_on:
      - db
volumes:
  db_data:

The fpm variant ships no web server, so it needs an Nginx container that passes PHP requests to it. If you do not want to configure Nginx, the Apache variant (wordpress:php8.4-apache) serves WordPress directly on port 80 of the container.

#Bind mounts or named volumes for WordPress

Volumes in Docker Compose matter a lot for WordPress development. You need to mount your themes and plugins directory to edit code live, while database data must persist in named volumes so nothing is lost when the container restarts.

The right strategy balances performance and function. For files you edit often (themes, your own plugins), use bind mounts. For data that only needs to persist (database, uploads), use named Docker volumes. The official image documentation gives the paths: themes go in /var/www/html/wp-content/themes/ and plugins in /var/www/html/wp-content/plugins/.

#Credentials in .env files with Docker Compose

Secret handling matters even locally. Use .env files for database credentials and API keys, and make sure those files are listed in your .gitignore. Docker Compose reads the .env file in the project directory and substitutes variables such as ${DB_PASSWORD} inside compose.yaml, which keeps sensitive configuration apart from versioned code.

#Environment variables of the official WordPress image

The wordpress image on Docker Hub generates its own wp-config.php from environment variables. According to the official image page, these are the ones it reads:

VariableWhat it does
WORDPRESS_DB_HOSTService name or host of the database
WORDPRESS_DB_USER and WORDPRESS_DB_PASSWORDDatabase credentials
WORDPRESS_DB_NAMEDatabase name, which must already exist
WORDPRESS_TABLE_PREFIXTable prefix instead of wp_
WORDPRESS_AUTH_KEY and the other keys and saltsIf you leave them out, the image generates random values
WORDPRESS_DEBUGAny non-empty value turns on WP_DEBUG
WORDPRESS_CONFIG_EXTRAExtra PHP code evaluated inside wp-config.php

The detail that wastes the most time: the WordPress container does not create the database named in WORDPRESS_DB_NAME. The database container creates it, through MARIADB_DATABASE in MariaDB or MYSQL_DATABASE in MySQL. If the two names differ, WordPress shows a connection error even with correct credentials.

WORDPRESS_CONFIG_EXTRA is for constants the image does not expose, for example WP_ALLOW_MULTISITE or WP_ENVIRONMENT_TYPE. Whatever you put there runs through eval(), so keep it short and versioned with the rest of the configuration.

#How to make WordPress wait for MariaDB in Docker Compose

A plain depends_on is not enough. The Compose documentation on startup order explains that Compose waits for the dependency container to be running, not for it to be ready to accept connections. MariaDB needs a few seconds to initialise on first start, and WordPress can boot earlier and fail to connect.

The fix is a healthcheck on the database and condition: service_healthy on WordPress. The official MariaDB image includes a healthcheck.sh script for this, and the MariaDB documentation publishes an example with WordPress. Adapted for a development environment it looks like this:

services:
  db:
    image: mariadb:11.4
    environment:
      MARIADB_DATABASE: wordpress
      MARIADB_USER: wordpress
      MARIADB_PASSWORD: ${DB_PASSWORD}
      MARIADB_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      start_period: 10s
      interval: 10s
      timeout: 5s
      retries: 3
  wordpress:
    image: wordpress:php8.4-apache
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
      WORDPRESS_DEBUG: '1'
    volumes:
      - wp_data:/var/www/html
      - ./my-theme:/var/www/html/wp-content/themes/my-theme
    depends_on:
      db:
        condition: service_healthy
volumes:
  db_data:
  wp_data:

With this file, docker compose up -d starts the database, waits for the healthcheck to pass and only then starts WordPress on http://localhost:8080. Your own theme is a bind mount, and the rest of WordPress sits in the wp_data volume.

#WordPress Docker image variants: apache, fpm, alpine and cli

The official image is published in several variants. Tags combine the WordPress version, the PHP version and the type, for example php8.4-apache, php8.4-fpm, php8.4-fpm-alpine or cli.

  • apache: WordPress and Apache in one container. The most direct option for local development.
  • fpm: PHP-FPM only, meant to sit behind Nginx or another proxy.
  • fpm-alpine: the same on Alpine Linux, with a smaller image.
  • cli: WP-CLI without a web server, for running wp commands against an existing install.

The cli variant has a trap documented on Docker Hub: it is based on Alpine and its www-data user has UID 82, while the Debian-based variants use UID 33. Running wordpress:cli against a Debian install usually needs --user 33:33, and sometimes -e HOME=/tmp, so WP-CLI can write to the files.

#How to add PHP extensions to the official image

The image does not include extra PHP extensions, even when popular plugins need them. Docker Hub gives the example that the image cannot send email on its own. To add extensions you build your own image with FROM wordpress:... and follow the instructions of the php image. To change directives like upload_max_filesize, copying a custom.ini into $PHP_INI_DIR/conf.d/ is enough:

FROM wordpress:php8.4-apache
COPY custom.ini $PHP_INI_DIR/conf.d/

If you want themes or plugins baked into the image for several installs, the documentation says to copy them to /usr/src/wordpress/, which is copied to /var/www/html/ on the container’s first start.

#wp-env, the official WordPress Docker environment

If you build plugins or themes and do not need to reproduce a specific server, wp-env saves you from writing compose.yaml by hand. It is a Node package maintained in the Gutenberg repository that runs Docker underneath. You install it with npm -g i @wordpress/env and start it with wp-env start from the plugin folder.

The environment runs at http://localhost:8888 with the user admin and the password password. Configuration goes in a .wp-env.json file at the project root:

{
  "$schema": "https://schemas.wp.org/trunk/wp-env.json",
  "core": null,
  "phpVersion": "8.3",
  "plugins": [ "." ],
  "config": {
    "WP_DEBUG_LOG": true
  }
}

core set to null uses the latest stable WordPress release, phpVersion pins the PHP version and plugins lists the plugins to install and activate. There are also themes, mappings for mounting directories, port (8888 by default), testsPort (8889) and mariadbVersion.

Composer, PHPUnit and WP-CLI come with the cli container: wp-env run cli wp plugin list or wp-env run cli --env-cwd=wp-content/plugins/my-plugin composer install. Xdebug is installed but off, and you turn it on with wp-env start --xdebug. The documentation also describes an experimental mode, wp-env start --runtime=playground, which runs WordPress in WebAssembly without Docker, on SQLite and without the run command.

The practical difference from Compose: wp-env picks the architecture for you. For a plugin that is faster. To reproduce a production server with Nginx, Redis and an exact MariaDB version, Compose is still the right tool.

#VS Code dev containers for WordPress

Many developers no longer install PHP on their laptops. They use VS Code Dev Containers, which change how you work with the editor.

#How a dev container works in VS Code

You open your project folder, and VS Code detects the .devcontainer folder, boots Docker and connects the IDE directly into the container. VS Code extensions, command line tools and editor settings all run inside the container.

#Extensions and linters preconfigured in the container

A configured workspace for every project, as soon as you open the folder. Whether a developer joined the team five minutes ago or five years ago, the environment is identical for everyone. PHP extensions, linters, code formatters and debugging tools are defined in the repository.

#Dev containers for distributed teams

For WordPress agencies with distributed teams, dev containers remove a common source of friction: differences between local environments. Whoever opens the repository gets the same toolchain, without a “let’s set up your laptop” call.

#Docker performance on Mac and Windows

Docker used to be slow on non-Linux systems, because project files cross a virtual machine boundary. Docker Desktop now offers several ways to cut that cost.

#How to enable VirtioFS in Docker Desktop

On macOS, pick VirtioFS as the file sharing implementation in Docker Desktop settings. The Docker Desktop settings documentation states that VirtioFS has reduced the time taken to complete filesystem operations by up to 98%. It replaces the earlier options, osxfs and gRPC FUSE.

#Synchronized file shares for large repositories

For very large projects, Docker Desktop offers Synchronized file shares: it keeps a synchronized cache of the host files on an ext4 filesystem inside the virtual machine. Docker recommends it for repositories with 100,000 files or more, and it is available on the Pro, Team and Business subscriptions. A WordPress install with a large node_modules inside the theme can get close to that number.

#How much RAM Docker Desktop uses

The Docker Desktop virtual machine holds memory while it is running. Resource Saver mode stops it when no containers have been running for 5 minutes (adjustable), and Docker cites savings of 2 GB or more of host memory and CPU.

#Docker vs LocalWP vs DDEV for WordPress

ToolAudienceFlexibilityScalability
DockerProfessional DevsVery highHigh
LocalWPFreelancers/BeginnersLowMedium
MAMP/WAMPLegacy UsersNoneLow
DDEV / LandoDocker Power-UsersHighHigh

#Docker Compose compared with DDEV and Lando

DDEV and Lando simplify Docker for WordPress developers. They ship preset configurations and short commands that lower the learning curve. When you need full control over every piece of the infrastructure, Compose with your own file is still the most flexible option.

#When to choose DDEV and when Docker Compose

The choice depends on your experience and the project. If you are new to containers, DDEV has a gentler learning curve. If you run several projects with different infrastructure needs, Docker Compose with your own configurations gives you more room.

#Security in Docker environments for WordPress

Docker isolates by default, but configuration decides how much protection you get.

#Containers without root and up-to-date images

Avoid running processes as root inside your containers when there is no need. Keep base images updated to get security patches; the wordpress image documentation itself reminds you to rebuild and redeploy regularly. Scan your images for known vulnerabilities with tools such as Docker Scout or Trivy.

#Separate Docker networks per project

Compose creates its own network for each project. Keep that separation and do not attach containers from different projects to the same network without a reason, so a breach in one does not reach the others.

#Docker secrets and .env files for passwords

Never put credentials directly in your Dockerfile or compose.yaml. The wordpress image accepts the _FILE suffix on its variables: WORDPRESS_DB_PASSWORD_FILE=/run/secrets/db_password reads the password from a file instead of a variable. Compose mounts each secret at /run/secrets/<name> inside the container, according to its secrets guide, which adds one limit: secrets only work in Linux containers.

#How to set up WordPress CI/CD with Docker

The biggest win with Docker is that your CI/CD pipeline (GitHub Actions, GitLab CI) can use the same images you use locally. That consistency removes a whole category of deployment bugs.

#Steps of a CI/CD pipeline with Docker

  1. Run PHPUnit tests in the container to check backend logic.
  2. Run Cypress or Playwright end-to-end tests against the container.
  3. Deploy the stable image to your production cluster (Kubernetes/Swarm).

#Private Docker image registry for WordPress

Teams that build their own WordPress images keep them in a private container registry. Those images carry the needed PHP extensions, development tools and project configuration, so a new team member pulls the image instead of building it.

Docker Hub also describes a static deployment mode: build an image with WordPress, the theme and the plugins inside /usr/src/wordpress, run it with --read-only and tmpfs for /tmp and /run, and update by deploying a new image instead of relying on automatic updates.

#WordPress on Kubernetes or Docker Swarm

For high-traffic WordPress sites, orchestration with Kubernetes or Docker Swarm lets you scale with demand. When traffic rises during campaigns or sales events, new containers start to absorb the load.

#How to debug WordPress in Docker

Debugging WordPress inside containers takes a few techniques that differ from traditional local development.

#Xdebug inside containers

Setting up Xdebug in a container needs attention to networking: the container has to reach the IDE listening on the host. Many dev container setups already ship Xdebug, wp-env turns it on with --xdebug, and in your own image you need to install the extension and configure the client host and port.

#How to read logs with docker compose logs

Docker gives central access to the logs of every service. Use docker compose logs to see combined output from WordPress, Nginx, MariaDB and Redis, or docker compose logs -f wordpress to follow one service. With WORDPRESS_DEBUG on, PHP notices show up in that same output. In production, tools such as Grafana and Prometheus provide monitoring dashboards.

#Query Monitor and Blackfire in Docker

Tools such as Query Monitor and Blackfire work inside Docker WordPress environments. Profiling inside the container gives a picture of performance under conditions close to production.

#Migrating from MAMP or LocalWP to Docker

If you use MAMP, XAMPP or LocalWP, moving to Docker is simpler than it looks. The usual process is to export the databases, write Compose files that match your current setup and adopt the remaining practices step by step.

#How to move a pilot project to Docker Compose

Start with a single pilot project. Write a compose.yaml that matches your current environment, including the PHP version, the database engine and the extensions you need. Once the pilot works, move the other projects.

#How to import the database and wp-content

Export your databases with mysqldump and import them into the MariaDB container. Copy your WordPress files (especially wp-content) into the Docker volume layout. If the URL changes, for example to http://localhost:8080, run wp search-replace with WP-CLI and check that media and links work in the new environment.

#Conclusion

Docker is no longer just for DevOps; it is a fundamental skill for WordPress developers in 2026. With containers you remove environment bugs, speed up onboarding and build more secure applications. The time spent learning Docker comes back in productivity and code quality.

Read more about my WordPress speed optimization services and how containerization feeds into the overall performance of your site.

Are you still fighting with local server configurations? It’s time to containerize your setup with Docker.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Want this implemented on your site?

If you want to convert the article into a working site improvement, redesign, or build plan, I can define the scope and implement it.

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Frequently asked questions

Practical answers to apply the topic in real execution.

Docker performance on macOS in 2026#
With VirtioFS enabled in Docker Desktop, Docker states that filesystem operations take up to 98% less time. For very large repositories there is also Synchronized file shares, on the Pro, Team and Business plans.
Why use Docker instead of LocalWP#
While LocalWP is great for beginners, Docker offers full control over server versions, extensions, and network configurations, mirroring your production server.
Difference between an image and a container#
An image is a read-only template (like a blueprint), while a container is a running instance of that image (the actual house).
Docker and the security of a WordPress environment#
Isolating services (the database and the web server) in separate containers reduces the attack surface and keeps the local system clean. Passwords belong in secrets or in a .env file kept out of Git.

Need an FAQ tailored to your industry and market? I can build one aligned with your business goals.

Let’s discuss

Related Articles