How to Deploy Your WordPress Site with Docker

Overview

The beauty of Docker is how portable your applications become. Beyond that it also allows to version control our entire instance, providing a mechanism for quick rollbacks, updates, and audits. This tutorial will show you how to host your WordPress site with Docker by creating an image for it and deploying it.

Run WordPress in Docker

The simpliest example of running a WordPress site in Docker is too pull down the official WordPress image.

docker pull wordpress

If you prefer to use a specific version of WordPress, we can specify that when we pull an image. For example, we can pull down version 4.9.5.

docker pull wordpress:4.9.5

To run a WordPress container from the image we pulled down, we runt he following command.

docker run -d -p 80:80 wordpress:4.9.5

Verify that the image is running by using the docker ps command. The output will show you the containers state, name, and network information.

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
41442d5c2e9b        wordpress:4.9.5     "docker-entrypoint.s…"   3 seconds ago       Up 2 seconds        0.0.0.0:80->80/tcp   awesome_tu

Great. We can see our WordPress container is running. Let’s test that we can access the site from a web browser.

Great. We can see our WordPress container is running. Let’s test that we can access the site from a web browser.

It looks like everything is working. We are able to deploy a new WordPress site, configure it, and post content. But what if you wanted a custom configuration out of the box, rather than a vanilla install? You will need to create your own Docker image based on the WordPress one you’re using.

Creating Your Dockerfile

  1. Create your workspace for building a new image.
    mkdir ~/Workspace/Docker/Wordpress
  2. Create a new empty file called Dockerfile in the workspace.
    touch ~/Workspace/Docker/Wordpress/Dockerfile
  3. Open the Dockerfile in a text editor.
    vi ~/Workspace/Docker/Wordpress/Dockerfile
  4. Add the following lines.
  5. Save your changes and exit the text editor.

  6. Build your custom WordPress image.
    docker build  -t myblog/wordpress:1.0.0 ~/Workspace/Docker/Wordpress

Running Your Custom WordPress Docker Container

Let’s run a container based on our new Docker image.

  1. Start a new container based on your image.
    docker run -d -p 80:80 myblog/wordpress:1.0.0
  2. Use docker ps to verify the container is running.
    docker ps
    CONTAINER ID        IMAGE                    COMMAND        CREATED             STATUS              PORTS                NAMES
    8a5cb628c0c0        myblog/wordpress:1.0.2   "docker-entrypoint.s…"   15 seconds ago      Up 13 seconds       0.0.0.0:80->80/tcp   nifty_hopper
    
    
  3. Open a web browser and check the site is accessible.

Customize Your WordPress Image

We made a new image based on the official WordPress one. However, there’s no difference between the two. Let’s customize ours to match our needs.

We’ll be seeding our new image with our theme pre-installed. We’ll also add a couple of plugins, too.

  1. In your Docker image’s workspace, create a new directory for your theme.
    mkdir -p themes/mytheme
  2. Create a new directory for your plugins.
    mkdir plugins
  3. Extract your theme into the themes directory or clone it from your repository.
    git clone http://github.com/example/mytheme.git themes/mytheme
  4. Extract each plugin in the plugin directory.
    cd plugins
    tar xvf path/to/plugin1.tar.gz
    tar xvf path/to/plugin2.tar.gz
    
  5. Use the COPY Dockerfile instruction to copy the theme and plugins to your image during build. Open your Dockerfile and add the following lines
    COPY themes/mytheme /var/www/html/themes/mytheme
    COPY plugins/plugin1 /var/www/html/plugins/plugin1
    COPY plugins/plugin2 /var/www/html/plugins/plugin2
    

Making Your Themes, Plugins, and Uploads Persistent

The problem with seeding your WordPress image with your themes, plugins, and uploads is that any changes to them will not persist. This is because Docker containers are ephemeral — all changes to it while running will be lost when the container is stopped.

By using Docker’s volumes feature we can ensure all changes to certain directories are preserved when we restart the container.

  1. Open your Dockerfile in a text editor.
    vi Dockerfile
  2. Add a volume for your themes, plugins. and uploads WordPress directories.
    VOLUME ["/wp-content/themes/mytheme","/wp-content/plugins","/wp-content/uploads"]
    
  3. Save your changes and exit the text editor.
  4. Build a new version of your WordPress image.
    docker build -t myapp:0.0.1 .

Let’s run the new image and test that all changes to themes, plugins, or uploads are persistent.

  1. Start a new a container based on your latest image.
    docker run -d -p 80:80 myapp:0.0.1
  2. Verify that the container is running.
    docker ps
    CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                NAMES
    8a5cb628c0c0        myblog/wordpress:1.0.2   "docker-entrypoint.s…"   15 seconds ago      Up 13 seconds       0.0.0.0:80->80/tcp   nifty_hopper
    
  3. Open the WordPress site in a web browser.
  4. Install a plugin.
  5. Upload a few images.
  6. Stop your WordPress container. First identify the docker images ID using the docker ps command, and use it with the docker stop command.
    docker ps
    docker stop nifty_hopper
  7. Verify that the container is no longer running.
    docker ps
    CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                NAMES
  8. View the contents of the plugins, themes, and uploads directories.
    ls ./plugins
    ls ./themes
    ls ./uploads
  9. Start a new WordPress container based on your image.
    docker run -d -p 80:80 myapp:0.0.1
  10. Open your WordPress site in a web browser.
  11. Verify that your plugins and images are still available.

Specifying Mount Targets

We now have persistent storage for parts of our WordPress site that need to persist longer than the lifecycle of our container. All volumes are written to an unspecified location on the filesystem managed by Docker when they are created. To specify the location on your filesystem you wish for the volumes to mount from, you can do so using the -v flag.

docker run -d -p 80:80 -v themes/mytheme:/var/www/html/wp-content/themes/mytheme -v plugins:/var/www/html/wp-content/plugins -v uploads:/var/www/html/wp-content/uploads myapp:0.0.1
  1. Open your WordPress site in your web browser by navigating to http://localhost/.
  2. Verify your theme is available.
  3. Verify your plugins are available.
  4. Upload some images to your WordPress instance.
  5. From the command line, verify that the images you uploaded are available in your uploads directory.

Lower Your Container’s Privileges

Your container will run as Root by default. This can lead to unexpected security vulnerabilites that can easily be thwarted by changing which user the container runs as.

Your container will run as Root by default. This can lead to unexpected security vulnerabilites that can easily be thwarted by changing which user the container runs as.

The Dockerfile has a USER instruction that allows you to specify a user with the least amount of privileges required to run your container. If you are mounting volumes from your filesystem, the user or a group it is a member will need access to the directories.

  1. Create a new user for your WordPress container.
    useradd wpuser1
  2. Grant ownership of the themes, plugins, and uploads directory the container will mount from to our new user.
    chown -R wpuser1 themes
    chown -R wpuser1 plugins
    chown -R wpuser1 uploads
  3. Add the USER directive to your Dockerfile.
    USER wpuser1

Running Containers in Production

In a production environment we want to ensure that our container is always running. Our WordPress site should be able to survive container failures and system reboots, for example.

We can achieve this in a number of ways. The simplest approach is to set the container’s restart policy. The following policies can be used.

off (Default)A container will not be restarted automatically
on-failureA container will restart automatically after a failure. A limit can be applied by appending :<int> to the end. For example, limiting restarts to 5 failures only we would use on-failure:5.
unless-stoppedA container will always restart automatically unless it has been manually stopped.
alwaysThe container will always restart itself

A good policy to begin with is the unless-stopped policy. This always ensures the container is scheduled to run, unless we stop it for administrative reasons.

We set the policy when the container is started using the -restart flag followed by the policy. The following example shows you how to set the restart policy to unless-stopped.

docker run -d -p 80:80 -restart unless-stopped myblog:0.0.1

Putting it all together our docker command for running WordPress would like like the following

Now, maintainability starts becoming a problem if we have to continue to remember to execute such a lengthy command. We can greatly simplify it all by introducing docker-compose.

Using Docker Compose

Docker compose is a tool built in Docker that allows us to define the starting conditions of our containers.

example

To start our WordPress site we now only need remember to run docker-compose up. All of the volumes and environment variables needed to run our WordPress site are defined the docker-compose.yml file.

Orchestration

While simply running the docker command directly and setting a restart policy or using Docker Compose is a good entry into running containers in production, the next evolution of your environment should be to introduce orchestration.

The two most popular orchestration solutions for Docker are Kubernetes and Docker Swarm.

This topic is beyond the scope of this tutorial. Setting up an orchestration layer is rather complex, and is unlikely to be beneficial when only running a few containers. It would not be a very cost optimized way of running your blog.

However, if you were running a large number of WordPress sites that needed high availability, then orchestration would be greatly beneficial.