This is the sixth post in a series on Modernizing my Personal Web Projects. In this post I’ll describe my experience of hosting WordPress on Kubernetes for my personal sites.
WordPress is a popular free and open-source content management system. It’s often used for blogs and e-commerce sites for its ease of use and vast plugin library. I moved to it myself for my personal blog some time ago because it takes care of all the backend parts that power the site – so I can focus on writing content.
Prerequisites for Hosting WordPress on Kubernetes
One of WordPress’s most powerful features is the ability to self-update and customize the source code from within the site itself. For this to happen it requires persistent disk storage – something which doesn’t typically mix well with cloud services. Still, with it being so popular, there is plenty of support for running it on Kubernetes. For this tutorial there are a few prerequisites:
- An external MySQL database such as DigitalOcean’s Managed Databases
- The NGINX Ingress Controller (see my guide).
- cert-manager for setting up HTTPS (see my guide).
- doctl
- helm
As usual I’ll be using Cloudflare for DNS and proxying.
Setting up the Database
The first thing to do is create the database that’ll be used for WordPress. If you don’t already have a managed database on DigitalOcean, you can create one with:
|
|
Next, create the WordPress database and user. To find the database ID use doctl databases list. Note the use of mysql_native_password – not all clients support the default password encryption yet, so this will be better for compatibility.
|
|
Then grant the user write access to the database by connecting to the database (see the ‘connection details’ in the DigitalOcean UI for instructions) and executing this SQL:
|
|
Migrating an Existing WordPress Database
If you have an existing WordPress installation (like me), you probably want to migrate its data to the new database. The standard MySQL tools can be used to do this:
|
|
Deploying the Helm Chart
Now it’s time to install WordPress on the cluster. I used the excellent chart from Bitnami – there are an overwhelming number of options available, though.
First, create a YAML file to override the values, named for example my_wordpress.yaml:
|
|
Most of the overrides are self-explanatory and can be customized. The critical setting is externalDatabase and mariadb.enabled = false, to make WordPress use an external database. If you didn’t migrate an existing database, the default settings will be used and you will be presented with the setup wizard when first connecting to the site.
Install the chart with:
|
|
Then navigate to your site (after setting up the DNS) to complete the installation.
Using DigitalOcean Spaces to Store Uploaded WordPress Files
For performance and cost reasons it’s a good idea to leverage DigitalOcean spaces to store uploaded WordPress media. This is more efficient than using a local block storage volume and allows you to serve files directly from the DigitalOcean CDN, taking load off the server.
There are expensive WordPress plugins that claim to do this, however there is an excellent free plugin I found: S3 Uploads by Human Made. There’s no admin UI but if you’re comfortable with the command-line that’s not an issue.
Installing the S3 Uploads Plugin
To install it:
- Download the manual-install.zip from the releases page
- Create a new directory called s3-uploads in wp-content/plugins
- Upload the .zip into wp-content/plugins/s3-uploads using a file manager plugin
- Extract the .zip file
After installing it, log on to the WordPress pod to configure the plugin:
|
|
Then apply a fix to the plugin by editing s3-uploads.php in the plugin editor in WordPress (otherwise the next command will fail):
|
|
Activate the plugin with
|
|
Then copy any existing media files
|
|
Using Private Uploads
If your bucket is not open to the public, you can secure the files using S3 presigned URLs. To do so, edit s3-uploads.php from the WordPress plugin editor and add:
|
|
Also, be sure to set the config setting so that newly uploaded files are marked private as well.
|
|
Potential Issues
There were a few other issues I came across, which may or may not apply to you depending on your site’s settings.
Copying Files into WordPress
If you have existing files to migrate (plugins etc.), copying them is a little tricky. I ended up using the Advanced File Manager plugin to copy over the files, which can be zipped and extracted on the server side. However, if you do this you’re likely to hit the next issue.
Max Upload Size / 413 Payload Too Large
If you need to upload large files, you might run into the default limit on the NGINX ingress. This can easily be worked around by adding an annotation on the ingress:
|
|
ABSPATH not working
I ran into an issue with the bitnami container due to using a symlink for wp-content. This changes the path used in __FILE__ / ABSPATH since they resolve the symlink, meaning that plugins and themes that rely on this method to find the WordPress root don’t work properly.
The fix I found was to change the use of __FILE__ where the error was occuring to use $_SERVER['SCRIPT_FILENAME']
instead. If you haven’t run into this issue, then you don’t need to worry.
Cloudflare Redirect Loop
When Cloudflare’s Flexible SSL mode is enabled, you might run into redirect loops when requesting certain files. I’m not sure of the root cause of this, but there are several plugins that fix it automatically. Cloudflare’s official plugin is one of them – just install it and you’re good to go. However, if you’ve followed the above steps then you can just use Cloudflare’s Full SSL mode instead.
Fixing Site Health Warning about WP_AUTO_UPDATE_CORE
If you see a warning on the dashboard like
The WP_AUTO_UPDATE_CORE constant is defined and enabled.
This can be fixed by setting the config value from within the WordPress pod, for example:
|
|
Wrapping Up
Hosting WordPress on Kubernetes takes a bit of initial setup and can be less convenient to work with than a traditional installation. However, the benefits of auto-failover and portability are worth the effort. Furthermore, you’ll be able to use this pattern for all your WordPress sites, keeping things consistent and easy to manage.