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