Move existing WordPress site into Docker

Existing wordpress site in Docker

I’ve been running two WordPress blogs for some time and my biggest regret is that they are not running in Docker containers. If I did the right thing in the beginning, I wouldn’t have to worry about whether or not the server upgrade will be safe, or will I be able to recall server configuration when time to migrate comes. I actually would be able to spin up local blog replica, run some experiments on it (new settings, features or design change) and decide whether or not I want move that change into ‘production’.

However, it’s never too late. I’m reluctant to make a big change on the real server without prior tests, so today I’ll try to create local Docker replica of one of my blogs and see how that goes.

Step 0: The idea

The overall process should be relatively simple, as WordPress already has its own Docker image. What’s more, they have a working example of docker-compose.yml file that spins up WordPress container along with mysql, so we have a nice starting point:

I think attaching my blog’s backup (content + mysql data) to containers would actually get most of the job done. There might be some issues with WordPress expecting true domain name while running on localhost, but we might be able to do something about it. So let’s begin.

Step 1: Backup the blog

What I like about WordPress is how easy it is to take its backup. Archive www folder, zip database’s dump, and it’s done. Surprisingly, this is also the thing that I don’t like about WordPress, as I’d rather prefer having clear separation of application from its configuration and data. It looks like there’s some sort of separation, but I’m not sure I can trust it.

So, let’s tar WordPress’s web folder and make mysqldump for codeblog.dotsandbrackets.com:

After these files ended up on local machine, we can move to the step two.

Step 2: Restore the website in a container

I checked WordPress’s Dockerfile and it seems that it will update wp-config.php file with environmental variables passed to container. Net effect is that we won’t have to restore mysql database with the same credentials as we used ‘in production’ and use new ones instead. We even can use root user (default choice for WP Dockerfile).

Very same Dockerfile says that it stores Wordpress’s content in /var/www/html (which is a volume, btw), so I can simply mount my backed up content to that path.

Finally, I’m not comfortable putting any credentials into files that most likely will end up in source control, so I’ll put them into environmental variables instead, e.g.:

Now, untar web content, create initial version of docker-compose.yml file with empty database for now and try to start it:

WordPress container: install

It did start, but we can’t tell if that install page is taken from ‘our’ content or from one that came with container. We need some data. Let’s shut down the web site and go to find some:

Step 3: Restore the database

Googling into mysql’s Dockerfile reveals wonderful picture: whatever we mount to /docker-entrypoint-initdb.d/ path inside of mysql container will be executed when container starts for the first time. It even understands archives! So theoretically, mounting mysql dump to that path should do the trick. Let’s try that. mysql service definition changes just a little bit:

Repeat startup process:

codeblog.dotsandbrackets.com in Docker

Cool! However, there’s one problem. If I try to login, it’ll redirect me to codeblog.dotsandbrackets.com. It’s clearly using hardcoded domain name and we need to fix that.

Step 4. Fix web site address

Quick googling into a problem immediately shows that the following sql queries might help:

mysql’s docker-entrypoint-initdb.d path can accept many files (in alphabetical order), so if I replace oldurl with codeblog.dotsandbrackets.com and www.newurl with 127.0.0.1, put the result into migrate.sql and mount it with - ./migrate.sql:/docker-entrypoint-initdb.d/migrate.sql, the problem should be solved. Let’s give it a try.

Migrate file:

New volumes component in mysql service:

Now, restart the web site:

This time it can navigate to login dialog and even login! But Jetpack plugin looks sad. I think for now I’ll just shut it down.

codeblog.dotsandbrackets.com: logged into container version

However, there’s bigger problem. Clicking on any article causes 404:

Links are broken

I vaguely remember configuring something in blog’s nginx properties when I installed it. Probably it’s time to head back to blog’s host and check out what was that.

Step 5. Fixing the links

OK, the problem’s just gotten a little bit bigger. Configuration line that made the links to work was try_files nginx directive:

But WordPress image that I used comes with Apache, not nginx. Apache does have similar configuration option, but I’m not that familiar with Apache to begin with, so let’s find another way.

WordPress image also comes with fpm configuration, which allows me to attach custom web server to it. I also found rault/nginx-wordpress image that sits and waits to be connected to wordpress:fpm image. It actually might help.

Again, before making any changes to YAML file, stop running containers first:

Then, add new service definition to docker-compose.yml:

Finally, start containers and keep all possible fingers crossed, hoping that it will work:

JetPack plugin is complaining on being in a sandbox again, but after disabling it one more time everything seems to be working now.

codeblog.dotsandbrackets.com in Docker containers

I clicked through the most obvious links, it all seems to be OK. In Docker and OK!

Conclusion

Moving existing WordPress into Docker containers is doable. Most of the process actually was quite straightforward and it’s only web server configuration step (probably specific to my site only) that puzzled me for a moment. I’ll spend some time looking for missing bits of configuration (e.g. host names), testing and measuring performance. But once I’m happy with all of that, I’m definitely moving that into ‘production’.

Leave a Reply

Your email address will not be published. Required fields are marked *