How to Deploy a WordPress website with Docker

Watch out! This tutorial is over 3 years old. Please keep this in mind as some code snippets provided may no longer work or need modification to work on current systems.
Tutorial Difficulty Level    

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.

The simplest 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 run the 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. If everything looks fine, we can now create your own Docker image based on the WordPress one you’re using.

Creating Your Dockerfile

Create your workspace for building a new image.

mkdir ~/Workspace/Docker/WordPress

Create a new empty file called Dockerfile in the workspace and edit as follows:

FROM wordpress

# Set environment variables used by the WordPress image
# DB_USER and DB_PASSWORD are included as an example. However,
# these should be removed and set during docker run.
ENV WORDPRESS_DB_HOST=127.0.0.1 \
    WORDPRESS_DB_USER=wpuser \
    WORDPRESS_DB_PASSWORD=super-secret-password \
    WORDPRESS_DB_NAME=wpsite \
    WORDPRESS_TABLE_PREFIX=wp_

COPY plugins/ /var/www/itlc.comp.dkit.ie/htdocs/wp-content/plugins
COPY themes/ /var/www/itlc.comp.dkit.ie/htdocs/wp-content/themes
COPY uploads/ /var/www/itlc.comp.dkit.ie/htdocs/wp-content/uploads

Now 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. Start a new container based on your image.

docker run -d -p 80:80 myblog/wordpress:1.0.0

You should not encounter any issues. Use docker ps to verify the container is running and 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.

In your Docker image’s workspace, create a new directory for your theme.

mkdir -p themes/mytheme

Create a new directory for your plugins.

mkdir plugins

Extract your theme into the themes directory or clone it from your repository.

git clone http://github.com/example/mytheme.git themes/mytheme

Extract each plugin in the plugin directory.

cd plugins
tar xvf path/to/plugin1.tar.gz
tar xvf path/to/plugin2.tar.gz

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.

Add a volume for your themes, plugins. and uploads WordPress directories to your Dockerfile.

VOLUME ["/wp-content/themes/mytheme","/wp-content/plugins","/wp-content/uploads"]

Now 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.

docker run -d -p 80:80 myapp:0.0.1

Open the WordPress site in a web browser, install a plugin andupload a few images.

Now identify the docker images ID using the docker ps command, and use it with the docker stop command.

docker stop nifty_hopper

Verify that the container is no longer running with docker ps

View the contents of the plugins, themes, and uploads directories.

ls ./plugins
ls ./themes
ls ./uploads

Now start a new WordPress container based on your image and verify that your plugins and images are still available!

Tip: 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. eg.

docker run -d -p 80:80 -v themes/mytheme:/var/www/itlc.comp.dkit.ie/htdocs/wp-content/themes/mytheme -v plugins:/var/www/itlc.comp.dkit.ie/htdocs/wp-content/plugins -v uploads:/var/www/itlc.comp.dkit.ie/htdocs/wp-content/uploads myapp:0.0.1

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.

Create a new user for your WordPress container and grant ownership of the themes, plugins, and uploads directory the container will mount from to our new user.

useradd wpuser1
chown -R wpuser1 themes
chown -R wpuser1 plugins
chown -R wpuser1 uploads

Add the USER directive to your Dockerfile like so:

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.

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

To put it all together (and keep things maintainable) we use Docker Compose, a tool built in Docker that allows us to define the starting conditions of our containers. Our new docker-compose.yml will look something like this:

version: "3.7"

services:
  wordpress:
    image: wordpress
    ports:
      - "80:80"
    environment:
      - WORDPRESS_DB_HOST=127.0.0.1
      - WORDPRESS_DB_USER=wpuser
      - WORDPRESS_DB_PASSWORD=super-secret-password
      - WORDPRESS_DB_NAME=wpsite
      - WORDPRESS_TABLE_PREFIX=wp_
    volumes:
      - /opt/wpsite/themes:/var/www/itlc.comp.dkit.ie/htdocs/wp-content/themes
      - /opt/wpsite/plugins:/var/www/itlc.comp.dkit.ie/htdocs/wp-content/plugins
      - /opt/wpsite/uploads:/var/www/itlc.comp.dkit.ie/htdocs/wp-content/uploads

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 in the docker-compose.yml file.