Ultimate WordPress Docker setup guide

Learn how to use Docker to host WordPress websites and blogs in this comprehensive guide. Docker is the perfect platform to host WordPress websites but it’s not without its complications. This guide will put you on the right track from the start.


WordPress Docker Benefits

My WordPress Docker guide has these features if you follow it through:

  • Setup multiple websites/blogs on even modest Virtual Private Servers (VPS)
  • Use the NGINX reverse proxy, so you don’t have to use different ports (80,8080,8081 etc.) for each site
  • Every site gets SSL encryption, this protects your wp-admin login and looks good for your visitors
  • Certificates issued that should be renewed automatically with Let's Encrypt
  • Uses MariaDB for efficiency and low resource usage
  • Each site gets the latest version of WordPress and its own environment to install themes and plug-ins
  • WordPress upload limit raised from the 2MB default, that would otherwise interfere with the website/blog operation
  • Data is kept separately in Docker data volumes, that will persist outside the container
  • Each site is separate with its own dedicated WordPress and database containers, making cross-site contamination less likely​
  • WordPress, themes and plugins can be updated using the built-in WordPress update mechanism as usual
  • Option to backup your WordPress volume and database

Like anything, if you find parts of this useful here, adapt it for your own needs, Docker makes that very easy to do.

With this guide and the power of Docker you could setup several sites in a short space of time, 30 minutes or less most likely.

What you don't get (yet!)

While I hope the above features constitute what I have called the ultimate guide, there are some things I will revisit when possible and aren’t covered currently:

  • MariaDB updates
  • Resource controls
  • Caching​ and tuning

Many of these things you would have to look at anyway at some point, irrespective of using Docker or not.

New April 2016:

See my "How to update docker containers"  post for instructions on updating MariaDB and WordPress

Also I assume you know how to update your DNS to point your domains to the server as well how to install Docker and Docker-Compose.

​WWW vs non-WWW for your site

​You should decide how you want your site to be referenced with your domain name and site. Do you want your site to be www.mydomain.com or without the www part which is called a 'naked domain'.

This is important as it can effect how search engines like Google identify your site and what may be seen as duplicate content, which you can get penalized for.

If you make this choice upfront it's a lot simpler, it's also has some relevance to nginx and certificates as well as WordPress itself.. We want visitors to be able to visit the site whether they use www or not and have the same experience. The best practice is also to let Google know via the Google Webmaster Tools how you reference your site.​

For more information on this topic, check out this page - WWW vs non-WWW – Which is Better For WordPress SEO?

Docker alternative for WordPress - rtCamp's EasyEngine

While you can get up-to-speed with Docker very quickly, another option I’d draw attention to is rtCamp’s EasyEngine. This is a more conventional means of setting up websites and blogs in minutes. EasyEngine can setup WordPress on NGINX in a single command, with their script taking care of everything else

I like EasyEngine and it’s a great option but I prefer the separation that Docker provides from the underlying OS. If you don’t want to get too involved in Docker, EasyEngine is well worth looking into.

How to setup WordPress with Docker

Hopefully this doesn't sound overly complicated but these are the basic steps:

  1. Create the required folders
  2. Setup NGINX​
  3. Setup MariaDB and WordPress​
  4. Create data volume containers​
  5. Create vhost.d and uploads.ini files​
  6. Start containers

Bear in mind, most of these steps just involve copying and pasting commands or creating files with the contents included here.

You may have to play around with some of these commands if you get unexpected results.  I am assuming you have a properly configured server with the latest versions of Docker and Docker Compose installed.  

Note - I haven't tested this setup with Docker Toolbox.

Step 1

First to get started create some folders that we will need later. Put these in a folder of your choice, like the home folder. Create these folders as follows:

mkdir nginx
mkdir certs
mkdir vhost.d
mkdir yourdomain1com
mkdir yourdomain2com (optional)

Give the domain folder(s) meaningful names, as Docker Compose will use them to distinguish the containers. These are all top level folders.

Step 2

In the nginx folder, create a docker-compose.yml file.  I assume you are using the nano text editor for these steps, substitute it with your preferred editor if needed.

cd nginx
nano docker-compose.yml

​Paste this to build into your yml file.  Change the <username> portion, to your actual home folder or wherever you decide to create these folders in the previous step .

image: jwilder/nginx-proxy:latest
restart: always
- "80:80"
- "443:443"
- /var/run/docker.sock:/tmp/docker.sock:ro
- /home/<username>/vhost.d:/etc/nginx/vhost.d
- /home/<username>/certs:/etc/nginx/certs:ro
- /usr/share/nginx/html
image: jrcs/letsencrypt-nginx-proxy-companion
restart: always
- /home/<username>/certs:/etc/nginx/certs:rw
- /var/run/docker.sock:/var/run/docker.sock:ro
- proxy

Note: Indentation is very important in docker-compose.yml, all code has to line up exactly or when you come to start the containers, it may produce errors.

What does this actually do?

Let’s walk through this configuration, while you don’t necessarily have to understand it all, it does help to know what’s going on. We are using Docker-Compose to orchestrate these multiple containers from this YAML file, which has a .yml extension.

Here we are first using Jason Wilder’s nginx-proxy image to build a container, which is well-regarded.  It uses something called docker-gen to automate reverse proxy configs for nginx. This makes things a lot smarter, as nginx-proxy responds as containers are started and stopped. This means less work for us! We are using port 80 and 443. To get through to your website or blog, traffic has to come through here first on those ports, which nginx-proxy then decides what to do with them.

We now declare the required volumes, the first line is what nginx-proxy needs to function, while vhost.d is needed to override the default upload limit and to support certificates, with a config file for every domain hosted. The certs folder is unsurprisingly where the Let’s Encrypt certificates are held. The /usr/share/nginx/html folder is required for certificates to be generated.

Now we build a second container, using the letsencrypt-nginx-proxy-companion image from Yves Blusseau, which allow the creation/renewal of Let's Encrypt certificates automatically. This is the container that actually creates and renews the certificates, working in conjunction with nginx-proxy. This also requires some volumes configured, with write access to the certs folder, as well as mounting the volumes from the proxy service.

Step 3

​In the folder you created earlier for your domain, create a new docker-compose.yml file with the following configuration.  Change the MYSQL password to something secure and unique.

cd ​yourdomain1com
nano docker-compose.yml
image: wordpress
restart: always
- mariadb:mysql
- VIRTUAL_HOST=yourdomain.com,www.yourdomain.com
- LETSENCRYPT_HOST= yourdomain.com,www.yourdomain.com
- LETSENCRYPT_EMAIL=email@domain.com
- 80
- 443
- yourdomain_wordpress_data
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
image: mariadb
restart: always
- MYSQL_ROOT_PASSWORD=<strong_password>
- MYSQL_DATABASE=wordpress
- yourdomain _mariadb_data

What does this actually do?

​Again, let’s look at what this does. This creates two containers, one for WordPress itself and the other running MariaDB as the database backend. 

You might be tempted to run everything in one container but the whole point of containers is to separate services, making them more manageable and predictable.

A couple of important thing to point out in the WordPress section, the VIRTUAL_HOST part is required by nginx to enable access your site.  This should match the LETSENCRYPT_HOST line as well.  Since we want visitors to be able visit the site using either www.mydomain.com or mydomain.com (see the discussion earlier about this), we specify both these names. 

List your preferred name first though, as the Let's Encrypt certificate will be created with this name​.  If you list a second domain, that will be added as Subject Alternative Name (SAN).  This just means the certificate will work with both www.mydomin.com & mydomain.com. Also specify an email address for the other field, which is required for the certificates. 

We expose port 80, 443, which allows nginx to communicate with the container and allow access to your site.  Both containers have their own data volumes, which we will cover later, as well as upload.ini which overrides some default WordPress settings. 

Steps 1-3 Demo

Rather than just talk about this configuration I can show you this in action as well with asciinema.org:

This is with my test domain bocker.xyz and setup using the root account.

Step 4

​Now we need the data volumes, so our WordPress and MariaDB containers can store their data independently of the corresponding container itself. 

docker create --name yourdomain_mariadb_data -v /var/lib/mysql mariadb
docker create --name yourdomain_wordpress_data -v /var/www/html wordpress

Step 5

​We are almost done! We now have a Docker Compose files, that will create nginx and a companion containers for certificates.  Then we have MariaDB and WordPress containers to host our site.

Now we create a file in the vhost.d folder from step 1, it must be named exactly after the VIRTUAL_HOST, we specified in step 3 

cd vhost.d
nano yourdomain.com

Then add these commands

server_tokens off;
client_max_body_size 100m;

You have to do the same again for each hostname e.g www.yourdomain.com, creating a corresponding file with the above contents. This means nginx will understand either address for your site.

Here is our uploads.ini, that overrides some rather limiting default PHP based WordPress settings. Without these tweaks you couldn't upload images, themes or plugins over 2 MB's in size!

Create the uploads.ini file in the yourdomain1com folder created in step 1.

cd yourdomain1com
nano uploads.ini
file_uploads = On
memory_limit = 64M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 1800

Step 6

​You can now start the containers, first nginx and then WordPress/MariaDB.

cd nginx
docker-compose up -d
​cd yourdomain1com
docker-compose up -d

​All being well, the containers will start up, if in doubt run docker-compose logs in the respective folder.  This will show you the combined logs of the containers, which will give you some clues on activity and any potential errors.

You can now visit https://www.mydomain.com or https://mydomain.com depending on how you decided to setup your site.  This may take a few minutes, while a certificate is issued via Let's Encrypt and the containers are ready.  

If everything has worked you will get the standard WordPress installation screen.  You can set the site title, the admin username and password, as well as an email address. If you got this far, you will now have a working WordPress site!

Steps 4-6 Demo

Again, here is the demo, that hopefully makes this process more understandable:

WordPress Backups with Docker

As you spend time on your site you are going to want a way to back it up.  There could be several ways to this but I am going to show you one way.

First lets backup the ​WordPress volume, specify the WordPress data volume from Step 4 and name the tar file after your domain to give it meaning.

mkdir backup
cd backup

Here the container we run only exists for as long it takes to backup the WordPress volume.

​docker run --rm --volumes-from mydomaincom_wordpress_data -v $(pwd):/backup wordpress tar cvf /backup/mydomaincom_wordpress.tar /var/www/html

Then we are going to backup the MariaDB WordPress database, changing mydomaincom and the strong password.  Note there is no space between this switch -pyourpassword. This is the password from Step 3 - 'MYSQL_ROOT_PASSWORD'.  This is executing a backup command in our MariaDB container.

docker exec -i mydomaincom_mariadb_1 mysqldump -u root -p<strongassword> wordpress > mydomaincom_mysql.sql

On your host in the backup folder, you will be left with two files - a .tar file and a .sql file giving a backup of your site.  This could be used to recover your site, or indeed move it to say another host.

Finishing up - Docker and WordPress a match made in heaven?

There may well be a few typos that have sneaked in but I'll be double checking to eliminate them if there are any!  

What I can say is, I have used the configuration on this very site, which has gone well so far. I have also run a second site alongside this, working well together by repeating steps 3-5 before starting everything up in step 6.

I hope this post has been of interest, I have tried to bring us much information together in one place to get you started with WordPress on Docker.  I will be revising the content, if there is anything else I can add value.  I'll have more Docker posts in the near future!