Tristan Penman's Blog

WordPress and Docker

01 December 2015

With so much of my recent work involving WordPress, it made sense to re-evaluate my workflow and figure out how it might be improved. This post shares some of the things I have found useful in setting up a Docker-based environment for WordPress.

MAMP

When I first picked up WordPress, I started using MAMP on Mac OS X, occasionally pushing code to a production environment. Besides being noticeably slower than a typically LAMP stack, MAMP for Mac hasn’t seen any meaningful updates in quite some time, and the non-pro version is limited to hosting one site at a time. I also found it to be quite cumbersome in terms of having a reproducible environment that I could share with others.

Prior experience told me that I could do better.

Enter: Docker

Using Docker, I created an image that provides a minimal environment for hosting a WordPress installation. This image, available on Docker Hub as tristanpenman/wordpress, can be used as the starting point for your own WordPress dev environment. The Dockerfile, and other parts of this image, are available on GitHub.

The image includes Apache and PHP, is configurable through various environment variables, and allows you to override all or part of the installation process using custom scripts or executables.

This is the Dockerfile that I have been using for development of a Wordpress plugin:

FROM tristanpenman/wordpress

# Disable mail function
COPY config/docker-php-disable-functions.ini /usr/local/etc/php/conf.d

# Remove unwanted plugins and install/activate others
COPY scripts/00-plugins.sh /scripts/post-install.d
RUN chmod +x /scripts/post-install.d/00-plugins.sh

This file defines a Docker image based on tristanpenman/wordpress. All this child image does is copy an installation script and configuration file into the container file system. The script itself installs WordPress, WooCommerce and activates the custom plugin. Not too shabby.

Check out the README file in the GitHub repo to see how that all works in more detail.

What about the database?

The database is where my projects vary the most. And I find that my database workflow for a client website is completely different from my workflow for plugin development. Thankfully, this Docker image is not particularly opinionated. It allows you to configure access to your database using environment variables, which seems to be the natural way to pass configuration to a Docker container.

An added bonus is that when linked to a container based on Docker’s official MySQL image, Docker can even set these environment variables automatically.

Docker Compose

With a reusable Docker container up and running, the next step was to find other tools in the Docker ecosystem that would make my life easier. One tool that has been particularly useful is Docker Compose.

Docker Compose allows you to capture the configuration for multiple Docker containers all in one place (a file named docker-compose.yml). For example, this is the docker-compose.yml file that I have been using for development of the WooCommerce Redirects plugin:

# docker-compose.yml
#
# Taken from https://github.com/tristanpenman/woocommerce-redirects
#
web:
  build: .
  ports:
   - "8080:80"
  volumes:
   - ./src:/var/www/html/wp-content/plugins/woocommerce-redirects
  environment:
   - WORDPRESS_DB_NAME=wordpress
   - WORDPRESS_DB_PASSWORD=wordpress
   - WORDPRESS_DB_USER=wordpress
   - WORDPRESS_SITE_URL=http://localhost:8080
   - WP_DEBUG=true
   - WP_DEBUG_DISPLAY=false
   - WP_DEBUG_LOG=false
  links:
   - mysql

mysql:
  image: mysql:5.7
  environment:
   - MYSQL_USER=wordpress
   - MYSQL_PASSWORD=wordpress
   - MYSQL_ROOT_PASSWORD=wordpress
   - MYSQL_DATABASE=wordpress

This file describes two containers - one for Apache/PHP/Wordpress, and another for MySQL. Notice that it also handles useful details such as port forwarding - in this case, requests to port 8080 on the host are forwarded to port 80 on the Docker container.

You may have noticed the build: . line. The WooCommerce Redirects repo includes a Dockerfile that performs a few extra installation steps (such as installing WooCommerce) and this line simply tells docker-compose to build a new image from the local Dockerfile (the one we looked at earlier) before creating a container. This is in contrast to the MySQL server, which uses the mysql image as-is.

Spinning up

With Docker and Docker Compose already installed, spinning up a fresh web server and database is as simple as:

docker-compose up

This spins up a development environment with a WordPress site that is accessible at http://localhost:8080.

Anecdotally, this Docker-based dev environment is faster than MAMP, with reduced CPU usage, and therefore extended battery life when working away from my desk. It is also much easier to spin up and tear down an environment. And although sharing the environment with someone else is still cumbersome, I love being able to track changes to the configuration in a version control system.

Gotchas

At time of writing, Docker is only officially supported on Linux. If you’re using Mac OS X, you will need to use a tool such as Docker Machine (previously boot2docker) to run Docker in a Linux VM. This is not as annoying as it sounds, as the Docker client CLI still runs locally.

One more gotcha with this is that you will need to set up port forwarding for the VM as well as your Docker containers. In the example above, Docker Compose is configured to forward requests on port 8080 of the host to port of the Docker container. You could use the IP address of the Linux VM directly, but with WordPress it is important that the URL you use in your browser matches the site URL of your WordPress installation.

Docker Machine’s ssh command makes this pretty easy. For example, to forward port 8080 on localhost to 8080 on the VM, I would run the command:

docker-machine ssh dockervm -L 8080:localhost:8080

(where ‘dockervm’ is the name of the Linux VM created by Docker Machine).

Future Improvements

An area that I would like to explore further is how this same image could be used in a production environment.

This hasn’t been a top priority for me, as I currently have little control over where my code is deployed. But there could be some benefit in using the same Docker image in development, testing and production. It also opens up the possibility of deploying WordPress sites to Cloud providers such as AWS or Azure.