How do I ‘containerize’ something, part 5: Docker and WordPress

This is part 5 of a series of posts tracking my efforts at taking a tiered application (WordPress) and stuffing it into containers for repeatable, automate-able deployment.

This is by far the simplest – or should be – part of the process. According to the official tutorial on migrating a WordPress site, really all we should need is two things – a new, clean, working installation of WordPress wherever we might like to migrate to, and the “wp-content” directory from our previous installation. As I mentioned in part 3, I already have a copy of my previous WordPress files, so all I should need to do is mount or copy the “wp-content” folder into a new installation of WordPress, right?

Well, of course I will need the database as well. Luckily I already have a functioning container running MySQL with the database. So I will update my local directory thusly:

tree -L 2
.
├── compose.yml
├── mysql-init-files
│   └── wordpress.sql
├── run
│   └── secrets
├── uploads.ini
└── wp-content
├── cache
├── index.php
├── plugins
├── themes
├── upgrade
└── uploads

10 directories, 4 files

… and I will update my Compose file thusly:

version: "3.8"

services:

wordpress:
image: wordpress:6.4.3-php8.2-apache
restart: always
ports:
- 80:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
volumes:
- ./wp-content:/var/www/html/wp-content
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
secrets:
- db_password


db:
image: mysql:8.3.0
restart: always
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD_FILE: /run/secrets/db_password
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
volumes:
- db:/var/lib/mysql
- ./mysql-init-files:/docker-entrypoint-initdb.d
secrets:
- db_password
- db_root_password

secrets:
db_password:
file: ./run/secrets/db_password.txt
db_root_password:
file: ./run/secrets/db_root_password.txt

volumes:
wordpress:
db:

I would like to point out a few things in this file…

  • WordPress environment “WORDPRESS_DB_HOST”-
    • Note that the environment variables I am setting for WordPress identify the database host by the name of the service – this will tell Docker to resolve the environment variable using the actual name of the container once everything is spun up.
  • WordPress environment “WORDPRESS_DB_USER” and “WORDPRESS_DB_NAME”
    • I have used “wordpress” for both – meaning WordPress will try to use the username “wordpress” to connect to a database named “wordpress.”
  • MySQL environment “MYSQL_DATABASE” and “MYSQL_USER”
    • perhaps it is self-evident, but these variables will actually create a new database and user with the name “wordpress…” That was not self-evident to me at first, but it is easy to verify simply by logging into the MySQL server and looking at the user table and the databases.
  • MySQL volume
    • As I pointed out in my last post, when you mount a directory with a SQL file or script to docker-entrypoint-initdb.d, Docker will execute those scripts upon startup. In my case, this will restore my previous “wordpress” database to the new “wordpress” database.

So, what happens when I bring it all up?

docker compose up -d

[+] Running 2/3
⠼ Network wp-migrate_default Created 0.4s
✔ Container wp-migrate-db-1 Started 0.3s
✔ Container wp-migrate-wordpress-1 Started 0.3s

Finally, because I configured WordPress to respond to “blog.occasional-it.com, I can also reach it using a FQDN (of course in this case I am just modifying my local hosts file):

Conclusion

At the end of the day, this was much easier than I was making it out to be… but then, I had to learn a bunch about MySQL, Apache and PHP, and not to mention Docker itself along the way. And learning about initialization scripts for MySQL was hugely important!

I wouldn’t consider this project ready for deployment to a production system yet, but I am well on the way. I have successfully captured my local WordPress installation and ‘containerized’ it with Docker. Next up will be pushing to a repository on Github, building the images using Github Actions, and deployment to a cloud service.

What did I learn?

  • There are a LOT of ways to containerize an application
  • There is no substitute for just knowing your application – in my case I had to re-learn WordPress, as it has been years since I actually had to deploy it myself (I run this blog on the commercial WordPress cloud service).
  • ASK FOR HELP! I don’t know how I would have learned about initialization scripts for MySQL without asking, or a LOT of reading first. My lesson? Ask someone who has done it before – it will save you time. 😉