How To Use Docker For WordPress Development
Tags: docker, wordpress
Published: 2020-05-07 22:04:27



When developing WordPress sites, apps, plugins and themes, it's always a good idea to work with a local test environment. When I started WordPressing seriously in 2014, the options were XAMP -- DesktopServer, MAMP, WAMP, etc. and Vagrant -- VVV, Homestead, etc. Later on Docker-based solutions appeared. For example, Local by Flywheel.

This post is an introduction to using Docker for local WordPress development. It's not going to teach anything about using Docker in production or make you a Docker expert. My goal is to understand what Docker does and how to quickly get a local development envioronment up and running with the tools for this provided by the WordPress open source project.

## Why Docker

XAMP like solutions install and configure PHP, MySQL and a web server -- apache, nGnix, etc -- **on your computer** to work as WordPress site. On the other hand, Docker creates and runs a complete web server stack -- including the operating system and all tooling -- in a completley isolated "container" that runs the same whether your computer is Linux, Windows, or Mac.

For teams of any size, the "works on my machine" problem will always be present. Using Docker allows us to explictily say "this is what the machine we develop with is." With Docker, everyone can choose there own computer, or possibly a cloud-hosted IDE and not worry about getting all of the parts of a web server set up the same way as there team mates every time.

Docker is used to "containerized" our applications. A container is one application, totally encapsulated into one immutable entity. We simplify the creation of containers with pre-defiend templates called images. For example, if we want to use PHP in our app, we could use our Dockerfile to install PHP, and extensions and configure it. Or we may use a PHP image, or a PHP-fpm 7.4 image. 

This is a [good article comparing Docker and Vagrant](https://deliciousbrains.com/vagrant-docker-wordpress-development/) from Delicious Brains.

## Basic Introduction To Docker

If you've ever followed a tutorial on setting up a server, you may recall beign given a set of instructions to follow, in the correct order to install all of the software via the command line. This always makes me nervous -- I'm worried about skipping a step or remembering to do it the same way every time.

Docker files normally start with a "FROM" directive, which tells us what "image" -- a template defined in a different Dockerfile -- to start from. This is like starting with a fresh VPS with Ubuntu installed.

```sh
# Start with Ubuntu installed
FROM ubuntu
```

The Dockerfile describes exactly how to make the server. The commands are similar. If we had a VPS, we might SSH in, and use apt-get to install some software, such as git. We use the RUN directive to do the same thing in our Dockerfile: 

```sh
# Start with Ubuntu installed
FROM ubuntu

# install git
RUN apt-get update \
    apt-get upgrade \
    apt-get install git
```

### A Few Key Docker Concepts
Docker is a technology that gets a lot of attention, beacuse it can do so much. It's a large ecosystem, I want to start with a few concepts that you should know. The goal here is to show what's important, and what can be ignored.

Docker is a software that manages containers. Container is a general term, not specific to Docker. Containers are not run by the operating system directly, instead they use the operating system's kernal directly and run there own OS. This makes containers, effectively different computers running on the same processor.

Docker, provides and API and CLI for creating and running containers both in production and on your computer, via [a desktop app](https://www.docker.com/products/docker-desktop). You should install the Docker desktop app for Mac, Windows or Linux.

[Intermodal shipping containers](https://en.wikipedia.org/wiki/Intermodal_container) come in a variety of standard sizes. They can be stacked in warehouses, on ships, placed on trains, or pulled behind a truck. What is in the container doesn't matter. A ship is built to carry X containers of size Y, what's inside those containers doesn't matter, as long as the dimensions, max weight and door specifications match.

You can think of Docker, the software as the crane that moves the containers around. We can do this from the command line, but that doesn't scale well past a single container. We need php, MySQL, etc for our apps. The docker ecosystem has various container orchestration tools that you can think of as telling the crane how to assemble stacks of containers.

There are several solutions we could use for orchestration. The simplest is docker-compose, which is optimized for local development. While docker-compose could be used in production, generally something like Kubernetes is a better choice in production.

With docker-compose, we can define a collection of services that work toghether into one container, [with a single yaml file](https://docs.docker.com/compose/compose-file/).

## Options For WordPress Local Development With Docker

Until recently, to use docker-compose with WordPress, we needed to write and maintain a docker-compose.yml file. The documentation includes a great guide, with examples of [how to write a docker-compose file for WordPress](https://docs.docker.com/compose/wordpress/). 

WordPress now has two node packages that use docker-compose, and remove the need to write that file. These packages do what I have spent a lot of time doing for my projects -- scripting installation, package.json commands, etc. But I don't have to maintain it and the open source project has done a better job than I ever did.

The rest of this post focuses on those options. Using your own docker-compose file is totally fine as well. I have also used [Lando](https://joshpress.net/create-a-wordpres-site-with-lando/) and [Local By Flywheel](https://localwp.com/), both of which work well. 

### Options For Docker-based Local Development From WordPress Core

The WordPress open-source project has published two tools to simplify docker-based local development. Both of them use nodejs to abstract away most of the complexity of using docker-compose for WordPress development.

The first option [@wordpress/env](https://www.npmjs.com/package/@wordpress/env) is the simplest option. It is useful for spinning up a quick test site with, some options. These options include installing plugins and switching WordPress versions.

The other option is the env sub command of [@wordpress/scripts](https://www.npmjs.com/package/@wordpress/scripts#env). This command is similar to @wordpress/env. It can also has a phpunit test runner that runs WordPress integration tests, making it more useful.

## Using @wordpress-scripts for Local WordPress Development With Docker

You may wish to review [this sample plugin](https://git.saturdaydrive.io/_/engineering-ops/cactus) I created while working through this section.

This section assumes you are already using @wordpress/scripts in your plugin for compiling JavaScript and CSS. If not install it first:

```sh
yarn add @wordpress/scripts
```

### Update Your package.json

WordPress env requires you add a "wp-env" key to your package.json and define at least two values. These required options are plugin name and directory. Assuming your plugin's name is Catus
```json
{
  "wp-env": {
    "plugin-dir": "cactus",
    "plugin-name": "Cactus"
  }
}
```

You also need to add an "env" or similar command to your plugin's scripts:

```json
{
  "scripts": {
    "env": "wp-scripts env"
  },
}
```

### Using The Env Command

To start and install the local docker-based development environemnt:

```sh
yarn env start
```
 
The first time this runs, the environment will be installed and started. In the furture it will re-start. You should see WordPress in your browser when you go to http://localhost:8889

To stop this environment, go to 

```sh
yarn env stop
```

*Featured image by [Dorothea OLDANI on Unsplash](https://unsplash.com/@dorographie?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText")*