How to Set Up a WordPress Development Environment with Docker (wp-env)

Introduction

Every WordPress developer needs a place to build and test sites without touching a live server. This article sets up that environment using Docker — specifically wp-env, the official local environment tool from the WordPress team.

Docker is a tool that packages software and everything it needs to run into isolated units called containers. A container is like a lightweight, disposable mini-computer: it has its own PHP, web server, and database, completely separate from the rest of your machine. When you are done, you throw it away, and your computer is left exactly as it was.

wp-env (the @wordpress/env package) uses Docker to spin up a complete WordPress site with a single command. You do not write any Docker configuration yourself — wp-env handles the containers for you.

This article walks through the whole setup:

  • Why Docker, and why wp-env on top of it
  • Installing Docker, Node.js, and wp-env
  • Starting a WordPress environment with one command
  • Configuring it with a .wp-env.json file
  • Running WP-CLI, troubleshooting, and managing the environment
  • A from-scratch Docker Compose alternative for when you outgrow wp-env

This article builds on “How to Install WordPress: Manual vs One-Click vs Local Installation,” which explains where local installation fits among the other methods. That article covers both desktop tools like Local and the Docker approach you will set up here.

Why Use Docker for WordPress Development

A Docker-based environment has real advantages once you work on more than one project:

  • Consistency. The container runs the same way on every machine. “It works on my computer” stops being a problem, because everyone on the team runs the identical environment.
  • Reproducibility. The environment is defined in a small config file you commit to version control. A new developer clones the repo, runs one command, and has the exact same setup.
  • Isolation. Each project runs in its own containers. One project’s PHP version or database never interferes with another’s.
  • Production parity. You can pin the PHP and database versions to match your live server, so code behaves the same locally and in production.
  • Clean teardown. Delete the containers and nothing is left behind on your machine. No leftover services running in the background.

The trade-off is that Docker adds one dependency you must install and keep running. For most modern WordPress teams, the reproducibility is well worth it.

Why Use wp-env Instead of Plain Docker?

You could run WordPress with plain Docker by writing your own configuration — and later in this article you will see how. So why use wp-env at all?

Docker is a general-purpose container platform. It knows nothing about WordPress. You have to tell it which images to run, how to connect them, and how to install WordPress on top. That is flexible, but it is work you repeat on every project.

wp-env is built specifically for WordPress development. It wraps Docker and does the WordPress-specific setup for you. Out of the box it provisions:

  • A WordPress installation
  • A PHP runtime
  • A MySQL database
  • WP-CLI, ready to run
  • A separate testing environment for automated tests
  • Automatic mounting of the plugin or theme you are working on

In other words, plain Docker gives you containers; wp-env gives you a working WordPress development environment. You reach for plain Docker (through Docker Compose) only when you need services wp-env does not provide — something we cover near the end.

What You Need First: Docker and Node.js

wp-env has two requirements. Install both before going further.

Docker

Install Docker Desktop from docker.com for Windows or macOS, or Docker Engine on Linux. Docker Desktop must be running before you start wp-env — wp-env talks to Docker in the background, so if Docker is not running, none of the commands will work.

Verify it is installed by running:

docker --version

Node.js and npm

wp-env is distributed as an npm package, so you need Node.js (which includes npm). Install the current LTS release from nodejs.org. Verify both:

node --version
npm --version

Installing Node.js is covered in more detail in its own article later in this guide. For now, the LTS installer from nodejs.org is all you need.

Step 1: Install wp-env

With Node.js in place, install wp-env globally so the wp-env command is available everywhere:

npm install -g @wordpress/env

Confirm it installed:

wp-env --version

You can also skip the global install and run it on demand with npx @wordpress/env, but a global install keeps the commands short for this walkthrough.

Step 2: Start a WordPress Environment

Create an empty folder for your project, open a terminal inside it, and make sure Docker Desktop is running. Then start the environment:

mkdir my-wp-project
cd my-wp-project
wp-env start

The first run takes a little while because Docker downloads the WordPress, PHP, and MySQL images. On later runs it starts in seconds.

When it finishes, wp-env prints the site URLs and login details in your terminal. Treat that output as the source of truth: the default URL is http://localhost:8888 (with the admin at /wp-admin), but the exact ports, admin username, and password can change between releases, so use the values wp-env actually reports.

Open the admin URL and log in. That is a complete WordPress install running in Docker, created with a single command. Depending on your version and configuration, wp-env may also start a separate testing environment on its own port for automated tests.

Step 3: Configure the Environment with .wp-env.json

On its own, wp-env gives you a plain WordPress site. The real power comes from a configuration file. Create a file named .wp-env.json in your project folder:

{
  "core": null,
  "phpVersion": "8.2",
  "plugins": [ "." ],
  "config": {
    "WP_DEBUG": true,
    "WP_DEBUG_LOG": true
  }
}

What each key does:

  • core sets the WordPress version. Leave it as null to always use the latest stable release — the right default for most development, and one that does not go stale. Pin it to a specific version or branch only when you are reproducing a production environment or testing compatibility against a particular release.
  • phpVersion sets the PHP version the container runs. Match this to your production server so code behaves the same in both places.
  • plugins is a list of plugins to install and activate. The “.” entry means “treat the current folder as a plugin” — more on that, and a common trap, in the next section.
  • config injects constants into wp-config.php. Turning on WP_DEBUG and WP_DEBUG_LOG here means you do not edit wp-config.php by hand. (That file is covered in “The wp-config.php File Explained.”)

You can also set port and testsPort to control which ports wp-env uses, which is handy when you run several projects at once. After changing .wp-env.json, apply it by restarting:

wp-env stop
wp-env start

Step 4: Develop a Theme or Plugin Live

Because your project folder is mounted into the container, you edit code on your machine with your normal editor and the container picks up the changes immediately.

For a plugin, the “plugins”: [ “.” ] line above mounts your project folder as a plugin. For a theme, point wp-env at the theme folder with the themes key instead:

{
  "core": null,
  "phpVersion": "8.2",
  "themes": [ "./my-theme" ]
}

A common beginner trap: “plugins”: [ “.” ] assumes the current folder actually contains a WordPress plugin — that is, a PHP file with a valid plugin header. If you drop that config into an empty folder and run wp-env, WordPress starts fine but your plugin never appears, because there is nothing there to load. People often read this as a bug when it is simply an empty mount.

The rule: map the folder that matches what you are actually building. Use plugins for a plugin, themes for a theme. You can also use the mappings key to mount any folder to any path inside the container (for example a shared wp-content or mu-plugins directory), but for most work the plugins and themes keys are all you need.

Step 5: Run WP-CLI Commands

WP-CLI is the command-line interface for WordPress, and wp-env runs it inside the container for you. Use wp-env run cli followed by the command:

wp-env run cli wp plugin list
wp-env run cli wp user list
wp-env run cli wp option get siteurl

What these do:

  • wp plugin list shows installed plugins and their status.
  • wp user list shows the site’s users.
  • wp option get siteurl prints the site URL stored in the database.

You never install WP-CLI yourself — it already lives in the container. WP-CLI is covered in depth in its own cluster, starting with “What Is WP-CLI? A Developer’s Introduction.”

Step 6: Manage and Reset the Environment

A few commands cover the whole lifecycle:

  • wp-env start — start (or restart) the environment.
  • wp-env stop — stop the containers but keep your data.
  • wp-env clean — reset the WordPress database back to a fresh install.
  • wp-env destroy — delete the containers and all data for the project completely.

Note the difference: stop is safe and keeps your content, while destroy wipes everything for that project. Reach for destroy when you want a genuinely clean slate.

Why Gutenberg Developers Use wp-env

If you plan to build blocks for the WordPress block editor, wp-env is worth adopting early. It is the environment most Gutenberg tooling assumes.

  • It is maintained by the WordPress project itself, alongside the block editor, so it tracks how core development actually works.
  • Most official Gutenberg and block examples use wp-env, so following along with the documentation is friction-free.
  • It pairs naturally with the standard block build tools, @wordpress/scripts and @wordpress/create-block.
  • It works well with automated testing, including the end-to-end tests the Gutenberg project relies on — which is why wp-env ships a dedicated test environment.
  • It is common in core and plugin contributor workflows, so learning it now prepares you to contribute later.

When you reach the block development articles in this guide, starting with “What Are Gutenberg Blocks? A Developer’s Introduction,” you will use wp-env as the foundation. Setting it up now means one less thing to learn then.

Troubleshooting: When Docker Runs but wp-env Fails

Even with Docker installed, wp-env can fail to start. Most of the time it is one of a handful of causes:

  • Docker Desktop is not fully started. The whale icon can appear while the engine is still booting. Wait until Docker reports it is running, then retry.
  • WSL issues on Windows. Docker Desktop runs on the WSL 2 backend. If WSL is outdated or stopped, Docker cannot start containers. Update it with wsl –update and confirm Docker Desktop’s WSL integration is enabled.
  • Port conflicts. If another process already uses the port wp-env wants, startup fails. Change port in .wp-env.json or stop the conflicting service.
  • Corrupted or outdated images. A half-downloaded image can leave the environment broken. Removing and re-pulling fixes it.
  • Stale containers. A previous run that did not shut down cleanly can hold onto resources or ports. Destroying and recreating clears them.

A few commands help you diagnose what is going on:

docker ps
docker images
wp-env destroy
wp-env start

What each one tells you:

  • docker ps lists running containers. Use it to see whether wp-env’s containers actually started, and to spot a stale container still holding a port.
  • docker images lists downloaded images. Use it to confirm the WordPress, PHP, and MySQL images are present and not corrupted.
  • wp-env destroy removes wp-env’s containers and volumes for the project, giving you a clean slate when something is stuck.
  • wp-env start rebuilds the environment from scratch, re-pulling anything missing.

When in doubt, wp-env destroy followed by wp-env start resolves most local issues — at the cost of resetting the database, so export anything you need first.

Alternative: A From-Scratch Docker Compose Setup

wp-env is the fastest path, but sometimes you want full control over the containers. For that, write your own docker-compose.yml. Docker Compose is a tool that defines and runs multi-container setups from a single file.

Reach for Docker Compose when your project needs services wp-env does not provide. Common examples:

  • Redis or Memcached for object caching
  • Mailpit or MailHog to catch outgoing email during testing
  • Elasticsearch for advanced search
  • phpMyAdmin for a database UI
  • A custom PHP container with specific extensions or settings

If all you need is WordPress, PHP, and MySQL, wp-env is simpler and quicker. Once you need extra services like these, a hand-written docker-compose.yml gives you that control. Start with a minimal base — just WordPress and a database — in a docker-compose.yml in an empty folder:

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wp_user
      MYSQL_PASSWORD: wp_pass
      MYSQL_ROOT_PASSWORD: root_pass
    volumes:
      - db_data:/var/lib/mysql

  wordpress:
    image: wordpress:latest
    depends_on:
      - db
    ports:
      - "8000:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wp_user
      WORDPRESS_DB_PASSWORD: wp_pass
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - ./wp-content:/var/www/html/wp-content

volumes:
  db_data:

What this defines:

  • db is a MySQL container. Its data is stored in a named volume (db_data) so it survives restarts.
  • wordpress is the official WordPress image, connected to the database through environment variables, with the site exposed on port 8000.
  • The ./wp-content volume mounts your local wp-content folder into the container, so your themes and plugins live on your machine.

Start everything with:

docker compose up -d

A note on syntax: modern Docker releases use docker compose (two words, a built-in subcommand). Older documentation uses docker-compose (hyphenated, a separate standalone tool). They do the same thing, so if you see the hyphenated form online, it is just the older version — prefer docker compose on current installs.

Visit http://localhost:8000 to finish the WordPress setup wizard. Stop and remove the containers with docker compose down (add -v to also delete the database volume).

Adding a service is just another block. The point of Compose is that you add only what you need, so when you actually want a database UI, append a phpMyAdmin service:

  phpmyadmin:
    image: phpmyadmin:latest
    depends_on:
      - db
    ports:
      - "8081:80"
    environment:
      PMA_HOST: db

Run docker compose up -d again and phpMyAdmin is available at http://localhost:8081. The same pattern applies to Redis, Mailpit, or any other service: add a block, map a port, and bring it up. That is the whole reason to drop down to Compose — you decide exactly what runs.

When to choose which: use wp-env for everyday theme and plugin development, where its WordPress-aware defaults save you time. Reach for a hand-written docker-compose.yml when you need extra services or precise control over the stack.

How Teams Typically Use wp-env

On a team, wp-env’s real value is that everyone runs the same environment from the same config. A typical onboarding looks like this:

  1. Clone the repository
  2. Install dependencies (npm install)
  3. Run wp-env start
  4. Begin development
  5. Commit code and the .wp-env.json configuration together

Because .wp-env.json lives in the repository, the PHP version, WordPress version, plugins, and constants are identical for every developer. A new teammate does not install PHP, MySQL, or WordPress by hand — they clone, start, and they are running the same stack as everyone else. That is the same reproducibility you want between local and production, applied across your whole team. Keeping this config in version control is covered in “How to Use Git and GitHub for WordPress Projects.”

Best Practices

Commit your environment config. Check .wp-env.json (or docker-compose.yml) into version control so the whole team shares one setup. This is the main reason to use Docker in the first place.

Match PHP and database versions to production. Set phpVersion and the database image to the versions your live host runs, so you catch compatibility issues early instead of at deployment.

Keep Docker Desktop running. wp-env and Compose both need the Docker engine active. If commands hang or error, check that Docker has fully started.

Know where your data lives. Database content lives inside Docker volumes, not in your project folder. Only your code (themes, plugins, wp-content) is on disk, so keep that in version control and export the database if the data matters.

Use one project folder per site. Give each site its own folder and its own ports. This keeps environments isolated and avoids port clashes.

Common Mistakes

Forgetting to start Docker. wp-env start fails with a connection error if Docker Desktop is not running. Start Docker first and wait for it to report ready.

Mapping the wrong folder. Using “plugins”: [ “.” ] in a folder that is not actually a plugin, or pointing plugins at a theme, means nothing loads. Map the folder that matches what you are building.

Expecting data to persist after destroy. wp-env destroy and docker compose down -v delete the database. Use stop (or down without -v) to keep your data between sessions.

Editing files inside the container. Always edit code in your project folder on disk, not inside the running container. Changes made inside a container are lost when it is rebuilt.

Hard-coding versions you forget to revisit. Pinning core or phpVersion to match production is good practice, but revisit those pins when production upgrades, or your local environment will silently drift out of date.

FAQ

Do I need to know Docker in depth to use wp-env?

No. wp-env hides Docker behind simple commands like wp-env start and wp-env stop. You only need Docker installed and running; wp-env manages the containers for you.

Which WordPress version does wp-env install?

By default, the latest stable release (core set to null). Pin core to a specific version or branch in .wp-env.json only when you need to match production or test compatibility against a particular release.

Where is my site data stored?

Your code (the plugins and themes you mount in) lives in your project folder on disk. The WordPress database and core files live inside Docker volumes. That is why wp-env destroy can wipe the database while your code stays safe on your machine.

How is this different from a desktop app like Local?

A desktop app such as Local hides the environment behind a graphical UI. wp-env is command-line and Docker-based, which makes the environment reproducible and easy to commit to version control. wp-env is also the official tool used by the WordPress project itself, which is why it pairs especially well with block and plugin development. Both are valid — wp-env is the better fit once reproducibility and Gutenberg work matter to you.

Can I run more than one site at a time?

Yes. Each project is its own folder with its own config. Give each one different ports (set port and testsPort) and you can run several environments side by side.

Does wp-env work offline?

After the first run downloads the Docker images, you can start and stop the environment offline. You only need a connection to download images or install plugins and themes from the internet.

Should I commit the whole environment or just the config?

Commit .wp-env.json (and your package.json) — that is all a teammate needs to recreate the environment. Do not commit the Docker images or the database; those are generated locally on each machine. Committing just the config is what keeps the setup reproducible without bloating the repository.

Conclusion

Docker gives you a WordPress development environment that is consistent, reproducible, and easy to throw away. wp-env, the official tool from the WordPress team, adds WordPress-aware defaults on top of Docker so you get all of that from a single command — plus WP-CLI, a test environment, and live plugin or theme mounting.

The setup is short:

  1. Install Docker and Node.js
  2. Install wp-env with npm
  3. Run wp-env start in a project folder
  4. Add a .wp-env.json to pin versions (or leave core as null) and mount your theme or plugin
  5. Use wp-env run cli for WP-CLI, and wp-env stop / destroy to manage the environment

For full control over the stack, a hand-written docker-compose.yml gives you the same containers plus extra services like Redis, Mailpit, or phpMyAdmin. And because wp-env is what the WordPress project itself uses, the time you spend learning it now pays off directly when you start building Gutenberg blocks, plugins, and themes later in this series. The next article, “WordPress File and Folder Structure Explained for Developers,” walks through what all those WordPress files and folders actually do.