Quick WordPress Install with Docker

I’ve just put together a WordPress site for my older daughter. It’s hosted on DigitalOcean and all of the infrastructure is handled with Docker. This post describes the steps in the (easy) install process.

The first thing that you need to do is install Docker. With that in place it’s a simple matter to instantiate a couple of images and the whole thing is up an running.

The Docker logo.

MySQL Docker Container

WordPress stores content in a MySQL database. Since we want to persist that data beyond the lifespan of a Docker container we should store the data on the host.

First create a folder for the database data.

mkdir ~/mysql-data

Next create a (temporary) environment variable to store the MySQL root user password. This is not really necessary, but it made my life easier.

export MYSQL_ROOT_PASSWORD="PercoreggIn2"

Now launch an instance of the MySQL Docker image. Note that the folder created above is listed as a volume and linked to the folder /var/lib/mysql on the container.

docker run --name mysql \
  -v ~/mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD \
  -d mysql:5.7.22

Check that the container is up and running.

$ docker ps
CONTAINER ID IMAGE            COMMAND                CREATED        STATUS        PORTS                NAMES
7937133554d9 mysql:latest     "docker-entrypoint..." 15 minutes ago Up 15 minutes 3306/tcp             mysql

We see that it’s listening on the (default) port 3306.

WordPress Docker Container

WordPress also stores content (templates, media etc.) in a folder. Again we want to persist this on the host, so we create a folder for the WordPress content.

mkdir ~/wp-content

Then launch the WordPress Docker image. The folder created above is listed as a volume and linked to the folder /var/www/html/wp-content on the container. This container is also linked to the mysql container.

docker run --name wordpress \
  --link mysql:mysql \
  -p 8080:80 \
  -v ~/wp-content:/var/www/html/wp-content \
  -e WORDPRESS_DB_NAME=wordpress \
  -d --rm wordpress:latest

Notes:

  • It’s not necessary to specify the WORDPRESS_DB_NAME environment variable, but handy to know what’s possible. There are a few other useful environment variables too.

Again we check that the container is up and running.

$ docker ps
CONTAINER ID IMAGE            COMMAND                CREATED        STATUS        PORTS                NAMES
707fcaad12e4 wordpress:latest "docker-entrypoint..." 20 minutes ago Up 20 minutes 0.0.0.0:8080->80/tcp wordpress
7937133554d9 mysql:latest     "docker-entrypoint..." 25 minutes ago Up 25 minutes 3306/tcp             mysql

Looks good. At this point we can do a local test on the host. Since the site is not yet visible from the outside world we’ll use lynx to open it.

lynx http://localhost/

If you get something that looks like a (text mode) web page then you are in business.

An Easier Way

A simpler way to set this all up is to use docker-compose. You just need to create a YAML file which defines the services.

version: '2.0'

services:

  wordpress:
    image: wordpress
    restart: always
    ports:
      - 8080:80
    environment:
      MYSQL_ROOT_PASSWORD: PercoreggIn2

  mysql:
    image: mysql:5.7.22
    restart: always
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: PercoreggIn2

Configuring NGINX

The NGINX logo.

Finally we need to expose the site to the outside world. If you are setting this up on an EC2 instance then you just need to make sure that port 8080 allows inbound connections (add a suitable security group) and you’re ready to roll.

I don’t pretend to be particularly competent with NGINX, but the configuration below worked for me, exposing the site on port 80.

upstream wordpress {
        server 127.0.0.1:8080 fail_timeout=0;
}

server {
        listen 80;
        listen [::]:80;

        location / {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                #
                # How long to wait [s] before giving a 504 error.
                #
                proxy_read_timeout 900;
                proxy_pass http://wordpress;
        }
}

Restart NGINX and the site will be live.

Separate Sub-Domain

I was so enthused by the ease of the above process (thank you, Docker!) that I decided to install a blog on another web site. In this case the main site is served on HTTPS. I didn’t have the time to sort out SSL for the blog, so instead I put it on a separate sub-domain. All that was required was an extra entry in the NGINX server block.

        server_name blog.example.com;

Conclusion

I’m once again astonished and thrilled by the ease with which things can be accomplished with Docker. Without it this install would probably have taken a couple of hours. However, I managed to research and implement all of this in less than an hour. And that’s a reflection on the tool, not on me!