Local WordPress Development with Docker and Docker Compose

Setting up a local environment for working with WordPress, or any development project can be time consuming. That typically means setting up a web server, a database, and support for all the languages/tools you’ll need. The problem is that installing this can get tricky, and they may not play nice with each other.

During the years i’ve been using everything from just running built in web server on my Mac, then switched over to using MAMP and then virtual machines using Vagrant. VVV, an open source Vagrant configuration for developing with WordPress is a good tool that we still use for some projects. Also, Trellis from Roots is another great tool for keeping development environments in sync between development, staging and producion servers.

Docker

But now it is 2018. I wanted something lighter and fast. Docker was the right choice.

Why Docker?

  • Create containers with isolated development environments.
  • Use fewer resources than with VMs.
  • Share your containers to make collaborations easier.

Once you get your head around this idea of having “containers” and how to “link” those containers, you’ll find out how easy is to build your own developing (or production) environment with docker.

Docker and WordPress

To run WordPress locally we need this to get started:

  • PHP
  • MySql
  • Nginx
  • Composer for dependency management (when using Bedrock)

Additional tools such as WP-CLI is also convenient to have. More about that later.

Setup a local domain and create a self signed certificate

We want to use a local domain ex mywebsite.local and also use a self signed cert for use https in our application. I often have a little cli folder in the project root with some scripts to get started.

If you like, use my scripts in my starter repo

Use the scripts like this:

  1. Create a SSL cert in ./certs using cd cli && ./create-cert.sh
  2. Trust the cert in macOS Keychain using cd cli && ./trust-cert.sh
  3. Setup vhost in /etc/hosts using cd cli && ./setup-hosts-file.sh

Docker Compose

I’m going to use a tool called Docker Compose which enables you to configure the services you want your container to have and set them up all at once.

Create a file called docker-compose.yml in your project root.

There is a bunch of things to cover here, so let’s break it down:

Nginx

Nginx (NGiИX) is our web server of choice.

  nginx:
    image: nginx:latest
    container_name: mywebsite-nginx
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - ./src:/var/www/html:rw,cached
      - ./certs:/etc/certs
    depends_on:
      - wordpress
    restart: always

Notes:

  • Use the nginx image from Docker Hub.
  • We are listening on both port 80 for http and 443 for https. More about setting up a local ssl cert later.
  • Use three volumes. 1. Load a custom nginx configuration. 2. Mount our src folder in /var/www/html and 3. Use local ssl-certs from ./certs

I’m using caching for our volumes to increase the performance

Nginx configuration

In ./nginx we also want to add our configuration. Very much just standard, and pointing to our certs.

Create the file wordpress_ssl.conf and add this config

Note: Since we use Bedrock, the web root is /var/www/html/web

Database

The database service is simple, just using the standard mariadb image and set some environment variables for password and database name.

  mysql:
    image: mariadb
    container_name: mywebsite-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_DATABASE=mywebsite
    restart: always
    ports:
      - '3306:3306'

WordPress

Our WordPress service configuration in this example is using the wordpress-php7.2-fpm.

  wordpress:
    image: wordpress:php7.2-fpm
    container_name: mywebsite-wordpress
    volumes:
      - ./src:/var/www/html:rw,cached
    environment:
      - WORDPRESS_DB_NAME=mywebsite
      - WORDPRESS_TABLE_PREFIX=wp_
      - WORDPRESS_DB_HOST=mysql
      - WORDPRESS_DB_PASSWORD=password
    depends_on:
      - mysql
    restart: always

Note: Although the default WordPress files will be downloaded in ./src when running docker-compose up, I’m ignoring them in git and will be using the Bedrock structure. If you are not using Bedrock, just use the defaults.

Composer

I use Bedrock for all WordPress projects, it’s a modern WordPress structure/setup using development tools, easier configuration, and an improved folder structure. Bedrock also offers enhanced security with isolated web root to limit access to non-web files and more secure passwords through wp-password-bcrypt.

Since we use Bedrock we need Composer to manage our WordPress installation and plugins with Composer, a PHP dependency manager. Composer will make development more reliable, help with team collaboration, and it helps maintain a better Git repository.

We set working_dir to /var/www/html where our WordPress installation is located.

  composer:
    image: composer/composer
    container_name: mywebsite-composer
    working_dir: /var/www/html
    restart: 'no'
    volumes:
      - ./src:/var/www/html:rw,cached

Full compose file

Here is the full docker-compose.yml file:

version: '3.6'
services:
  nginx:
    image: nginx:latest
    container_name: mywebsite-nginx
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - ./src:/var/www/html:rw,cached
      - ./certs:/etc/certs
    depends_on:
      - wordpress
    restart: always

  mysql:
    image: mariadb
    container_name: mywebsite-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_DATABASE=mywebsite
    restart: always
    ports:
      - '3306:3306'

  wordpress:
    image: wordpress:php7.2-fpm
    container_name: mywebsite-wordpress
    volumes:
      - ./src:/var/www/html:rw,cached
    environment:
      - WORDPRESS_DB_NAME=mywebsite
      - WORDPRESS_TABLE_PREFIX=wp_
      - WORDPRESS_DB_HOST=mysql
      - WORDPRESS_DB_PASSWORD=password
    depends_on:
      - mysql
    restart: always

  composer:
    image: composer/composer
    container_name: mywebsite-composer
    working_dir: /var/www/html
    restart: 'no'
    volumes:
      - ./src:/var/www/html:rw,cached

WordPress configuration

All our source code should be placed inside src folder. Add an existing project or initialize a new Bedrock project.

Environment configuration

Edit .env file and setup our credentials

DB_NAME=mywebsite
DB_USER=root
DB_HOST=mysql:3306
WP_HOME=https://mywebsite.local
DB_PASSWORD=password
WP_SITEURL=${WP_HOME}/wp
WP_ENV=development
...

Install extra packages and tools

Sometimes we need to use tools not installed in the standard Docker images. We can create our own Dockerfile, build it and use in Docker Compose.

Create a Dockerfile in our project root. Extend the base image and add your own tools.

Example:

FROM wordpress:php7.2-fpm

# Install sockets for PHP
RUN docker-php-ext-install sockets

# Install wp-cli
RUN curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
RUN chmod +x wp-cli.phar
RUN mv wp-cli.phar /usr/local/bin/wp

In our docker-compose.yml, modify the WordPress service a bit:

  wordpress:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: mywebsite-wordpress

    ...

When we make changes in our Dockerfile we can use docker-compose up -d --force-recreate --build to build our container again if we change something.

Install WordPress and plugins

Install new plugins with Composer, like this:

cd src
composer install

or

docker-compose run composer require wpackagist-plugin/wordpress-seo

Update WordPress and plugins

cd src
composer update

or

docker-compose run composer update

Create a new Bedrock project

If don’t have an existing composer.json file you can initialize a Bedrock project like this:

docker-compose run composer create-project roots/bedrock .

Start

docker-compose up -d

You can now browse to https://mywebsite.local and see your WordPress installation dialog!

Screenshot

Useful Docker and Docker Compose Commands

List Docker containers

docker ps

List Docker images

docker image ls

List compose containers

docker-compose ps

Stop

docker-compose stop

Down (stop and remove)

docker-compose down

Cleanup

docker-compose rm -v

Summary

There you go 😊

We have created a modern local WordPress development environment with Docker Compose. Checkout my demo repo on Github.

Let me know if you have any questions!