Docker and WordPress
I run a couple of WordPress websites. For the many years I’ve been using various shared web hosts to host the sites, but recently they started to fail me for various reasons. Fortunately, I use a great backup solution, so moving to another host is easy.
I decided to get myself a Virtual Private Server, or Virtual Machine, to host the websites myself. This would give me full control of how they were run, but I would be responsible for ensuring they were stable. I decided to use Docker to containerise the whole thing and make sure it was portable to another machine if I required it later.
Side note: I’ve signed up with BuyVM. They are very reasonably priced and so far are very reliable. I have previously used their shared hosting (which was very stable), but due to price changes with cPanel it worked out more efficient for me to get a VPS.
I signed up for the VPS and applied a Debian 9.0 64bit image. I booted up, installed Docker and some basic things like UFW firewall and then got started.
So how will this work?
- Traffic from the internet hits the Proxy. Only the Proxy is exposed to the internet. Everything else is only accessible internally. The proxy is running the nginx-proxy Docker container.
- The Proxy has a helper that speaks to LetsEncrypt to manage generating HTTPS certificates.
- The proxy determines which VM to send the request to. It does this by inspecting environment variables
- The appropriate WordPress container handles the request. Each WordPress container is running it’s own Apache server and is configured to speak to it’s own database container (running MariaDB).
I want to host multiple websites from the same VPS. To be able to handle multiple different hostnames, I needed to use
nginx-proxy to handle the incoming traffic and redirect it to the appropriate WordPress Docker instance. I also want to serve traffic over HTTPS, so I’m using
jrcs/letsencrypt-nginx-proxy-companion to inspect the contents of nginx-proxy and request the appropriate certificates from LetsEncrypt.
nginx-proxy instance observes the main Docker socket, so there is almost zero configuration required to add back-end instances. If I want to add other back-end services that I need to be public, not just WordPress, I can use the same setup to simply add new containers and everything works magically.
Everything goes together in a Docker Compose file:
version: "3" services: proxy: image: jwilder/nginx-proxy container_name: proxy ports: - "80:80" - "443:443" volumes: - certs:/etc/nginx/certs:ro - vhost:/etc/nginx/vhost.d - html:/usr/share/nginx/html - dhparam:/etc/nginx/dhparam - /var/run/docker.sock:/tmp/docker.sock:ro labels: - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy" https_proxy: image: jrcs/letsencrypt-nginx-proxy-companion container_name: https_proxy volumes: - vhost:/etc/nginx/vhost.d - html:/usr/share/nginx/html - certs:/etc/nginx/certs:rw - /var/run/docker.sock:/var/run/docker.sock:ro depends_on: - "proxy" volumes: certs: vhost: html: dhparam: networks: default: external: name: nginx-proxy
Running WordPress and MariaDB
I’m using the official WordPress Docker image. This runs a full Apache / PHP stack and latest version of WordPress. I’ve also decided to give each WordPress instance it’s own database instance. There is a bit of overhead running multiple database servers simultaneously, but I feel this is outweighed by the redundancy and simplicity of the stack.
version: '3.1' services: cjc_wp: depends_on: - db image: wordpress restart: always expose: - 80 links: - db:mysql environment: VIRTUAL_HOST: ceejaycee.net, www.ceejaycee.net LETSENCRYPT_HOST: ceejaycee.net, www.ceejaycee.net WORDPRESS_DB_PASSWORD: 'MYSECRETPASSWORD' WORDPRESS_DB_NAME: wp container_name: cjc_wp volumes: - /home/chris/config/data/wp:/var/www/html:rw - /home/chris/config/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini db: image: mariadb restart: always environment: MYSQL_ROOT_PASSWORD: 'MYSECRETPASSWORD' container_name: db volumes: - /home/chris/config/data/db:/var/lib/mysql:rw networks: default: external: name: nginx-proxy
We have created volumes for the Apache www folder. This allows it to be persistent across sessions and allows us to modify the content easily, if required. We also map in an
uploads.ini file, which allows us to override the default upload file size:
file_uploads = On memory_limit = 512M upload_max_filesize = 512M post_max_size = 512M max_execution_time = 600
Setting Up Each Site
I have all the individual WordPress instance content, plugins, themes and configuration backed up to Dropbox. There are two approaches to get each site up and running:
- Go to Dropbox, download the last backup. Copy the files to the Docker container. Then restore the database.
- Follow the WordPress install wizard. Install the UpdraftPlus plugin, authenticate with Dropbox, click “Restore”.
I usually follow step 2, because it’s much easier.
Backing up the Configuration
To ensure this configuration is reproducible and portable to other systems, it’s important to ensure the configuration is stored and backed up. I use a private GitHub repository to store the configuration. If I need to move this setup to another system, I can simply git clone the configuration files to another system, point the DNS to a new box, and it’s ready to go.
The important WordPress data (content, images, database, individual site configuration) is automatically backed up to Dropbox every night using a WordPress plugin.
And that’s all there is to it…
Note: some of the links on this post are affiliate links. If you sign up to use these services, I may get a small referral fee which will help keep this site alive.