WordPress on Kubernetes

The Definitive Guide for WordPress on k8s

Removing State from WordPress

In previous sections we’ve demonstrated how state gets in the way of scaling out WordPress pods in Kubernetes. In this section we’ll be looking at some ways to make WordPress into a stateless application, allowing us to use a Deployment (instead of a StatefulSet) and ultimately add more replicas.

Let’s look at all the components in a WordPress application, that make our container stateful.

WordPress Core Files

When running a WordPress container using an image from DockerHub, a specific version of the core software is already built-in, and can be updated by using a different container version.

This behavior in itself is quite stateless, however WordPress core allows administrators to update the core software from within the WordPress dashboard. Furthermore, a default WordPress installation will also attempt to automatically update itself to the most recent patch release, without any actions necessary from the admin.

These updates download fresh packages of the core software from the official WordPress website and replace/overwrite the existing files with new ones. Such updates are sometimes accompanied by database schema or other database-related updates too.

While this is great for the vast majority of WordPress users, in the case of horizontal scaling, updating files in our pods is what creates state. If we want to run an updated version of WordPress, we need to make sure that every pod is running the updated version of the core files.

There are a couple of ways we can achieve that:

  • Place the core files on a ReadWriteMany volume, attached to all running WordPress pods, so any update to these files are reflected on all pods.
  • Prevent WordPress from updating core files and manage core updates manually.

Both are viable solutions, although each has a pretty big tradeoff. In the first case, as mentioned in a previous section ReadWriteMany volumes tend to be more complex and very slow, especially at writes. This may significantly degrade performance, given that every WordPress page load will need to read potentially hundreds of PHP files.

In the second case, we’ll be losing the ability to update the software from the Dashboard, and we’ll also miss out on automatic security and maintenance updates, which means we’ll have to keep an eye on those, to perform them ourselves, via YAML manifest files for example. It does allow us to quickly roll back though, which is nice.

You can disable WordPress core updates with the WP_AUTO_UPDATE_CORE configuration directive. We’ll demonstrate this in the next section.

Themes and Plugins

Similar to WordPress core files, WordPress themes and plugins consist of some PHP, JavaScript, CSS and other files, providing extended functionality or presentation options for a WordPress application. These are typically stored in wp-content/themes and wp-content/plugins. There are also a few special cases, such as must-use plugins and drop-ins.

On a typical WordPress install, themes and plugins can be installed or removed through the Dashboard. Themes and plugins can also be updated through the admin dashboard. Furthermore, there have been cases where an automatic security patch has been shipped to a specific vulnerable plugin or theme, by the WordPress security team.

This creates a very similar situation to the WordPress core software, with regards to state. Unlike the core version however, the WordPress team does not publish themes or plugins to the Docker repository. Our options for dealing with such state are quite similar to the core files:

  • Place themes and plugins in a ReadWriteMany volume, attached to all WordPress pods.
  • Prevent WordPress from installing/updating/deleting plugins and themes and handle these manually.

Again, reading and executing any code from a ReadWriteMany volume is going to come with a significant performance hit. Writing files to such a volume (NFS or EFS for example) can take ages, compared to a single-attachment block volume (local or remote).

Managing themes and plugins manually will require some more work. We may choose to use a Dockerfile and burn these into the container, or use a Git repository with our themes and plugins and have some deployment system pull the contents when needed. We can also have a combination of both.

To disable theme and plugin (and translations) modifications and edits, we’ll need the DISALLOW_FILE_MODS and DISALLOW_FILE_EDITS directives in our wp-config.php file. We’ll demonstrate this in the next section.


When using WordPress in a non-US English language, the core software will attempt to load translation files from disk. These translation files can also be updated, without necessarily updating the core software. Translation files can also ship with plugins and themes, and can updated separately.

The way to deal with this state is going to be similar to the core software, themes and plugins. There are some things we can automate with regards to keeping translations updated and we’ll look at some of those tools in future sections.


In a stock WordPress installation, user uploaded files are stored in a special wp-content/uploads directory, however there are a few things in core, themes and plugins, that can affect the exact behavior when it comes to working with images specifically.

Image sizes (dimensions) for example, can be updated in the WordPress Dashboard. Themes and plugins can register their own custom sizes, and some plugins may even serve dynamic sizes.

Unlike with core files, plugins, themes and translations, we can’t really bake media uploads into a Git repository or a container. We can however place them onto a ReadWriteMany volume, or an Object Storage service, such as MinIO or S3.

Other Files

Depending on your exact configuration, themes and plugins, WordPress may try and write to other files in addition to the above.

For example, a page caching plugin can attempt to write files to wp-content/cache. Debug mode may attempt to write to wp-content/debug.log. Migration plugins may write to some migrations directory and so on.

It’s worth noting that some plugins are simply not designed to work in a stateless WordPress environment. A good example is a file-based page caching plugin. Even if you find a way to share the cache directory between multiple pods, the performance trade off is usually not worth it, and you’re better off with a different page caching solution.

What’s next?

As mentioned above, there is more than one way to achieve a stateless WordPress application, and each one has its pros and cons. In this guide we’ll cover a few different configurations or topologies, starting with the very classic option in our next section.

We’ll create a WordPress deployment that pulls our core software from DockerHub, our plugins, themes and translations from a GitHub repository, and our media from an S3-compatible storage which we’ll install into our Kubernetes cluster.