DevOops! found at Reddit

Deploying WordPress with Kubernetes and Terraform on AWS


Major cloud providers nowadays have decent hardware, network and 24/7 teams to operate your online business at a 99.9% uptime ratio for years, even without redundancy.

I myself have been running a managed cloud hosting provider for half a decade now and I can't even remember the last time a cloud instance went down for hardware-related issues. Amen. 🙏

- Why do I need Kubernetes then? You're probably wondering.

Unless you're running a million dollar business, the truth is you most likely don't. No kidding.

To make a looooong story short, Kubernetes was designed to run workloads such as Google's. It is a highly technical tool designed to enable the deployment, scaling and management of big boys applications.

The deployment described in this article is just a proof of concept, so you can understand a little more about scaling applications on the cloud using enterprise-grade technology.

Not that Enterprise though.
Not that Enterprise though. 🙁

It will look very simple and painless since the infrastructure is defined as code. Don't worry, you only need some knowledge of CLI to perform the steps.

WordPress Parts

Before going further, a quick reminder of how WordPress is structured so you can later understand better the infrastructure design:

  1. /wp-includes and /wp-admin directories && /*.php files
    These are CORE files, downloaded directly on team and are only updated when you update a WordPress install.

  2. /wp-content directory
    Location of the WordPress themes, plugins and uploads. These are the files that pretty much differentiate your site from a fresh WordPress install.

  3. MySQL database
    The users, posts, pages and options have to be placed somewhere and WordPress chose a long time ago to rely on a MySQL database to store all these configurations.

Pre-deployment Steps

You'll need to install the following softwares, preferably with the versions specified:

  • Terraform v0.14+
  • AWS CLI v2+
  • Kubectl v1.21+
  • AWS IAM Authenticator v1.19+
  • Helm v3+

How to install these softwares will largely depend on your operational system, but it's quite easy and I trust you can find the install instructions. 💪

Besides the softwares, you will also need a valid AWS account. After login, create an IAM user with Programmatic access only and AdministratorAccess policy. You can name that user terraform.

AWS will give you an access key ID and a secret access key when you create the IAM user. These credentials must be added to the .aws/credentials file.

The Terraform configuration used in this article relies on the AWS profile with name terraform-kube-wp-${var.stage}, so you can name the profile as such:

aws_access_key_id = XXX
aws_secret_access_key = XXX

The last item on the pre-deploy list is to clone the following repository:

$ git clone

It comes without saying but remember that AWS will charge you a couple bucks for running the infrastructure deployed here.


Terraform is the tool we use to define our infrastructure entirely as code. Each file on the repository is responsible for a part of the infrastructure. The EC2 instances, the database, the Kubernetes configs, even the DNS is managed by Terraform.

The file has some information you may want to change such as the stage-domain or the type of the instances.

Keep the stage as dev for now. To demonstrate how powerful Terraform is, the file allows you to specify different domains and instance types according to the environment you're deploying. Fancy or what? 😏

As long as you installed and configured everything mentioned on the previous topic, you just need to run two commands to deploy the environment: 

$ terraform init
$ terraform apply -auto-approve
It'll take some time, but that's pretty much it. The beauty of infrastructure as code!

After the Deploy

After Terraform deployed everything, you should be able to run commands with kubectl such as:

$ kubectl get nodes
NAME                         STATUS   ROLES    AGE   VERSION
ip-10-0-1-131.ec2.internal   Ready    <none>   55m   v1.18.9-eks-d1db3c
ip-10-0-2-20.ec2.internal    Ready    <none>   55m   v1.18.9-eks-d1db3c
ip-10-0-3-165.ec2.internal   Ready    <none>   55m   v1.18.9-eks-d1db3c

If you get one of the following error messages when running kubectl:

error: You must be logged in to the server (Unauthorized)
Unable to connect to the server: dial tcp: lookup on no such host

Don't worry, you just need to update the kubeconfig with AWS CLI:

$ aws --profile terraform-kube-wp-dev eks update-kubeconfig --name kube-wp --region us-east-1

When you look at the file and the k8s/ingress-controller.yaml file, you'll see instructions to uncomment after the first deploy:

That's because AWS will take a few minutes to deploy the load balancer, so we can only use it after the first deploy. As per instructions, uncomment both and run terraform apply again.

There are unofficial workarounds that makes Terraform wait for the load balancer deployment to create the DNS record, but to keep the code as simple as possible that workaround has been left out on purpose. 

Interesting details

WordPress Docker image from Bitnami is the image running on the Kubernetes cluster. On the k8s/ file it's possible to notice a couple environment variables:

These environment variables were added to allow WordPress to connect to the database. Speaking of which, the MySQL database running is powered by AWS Aurora MySQL-compatible, an AWS MySQL service offering capable of 5X (😱) the throughput of a standard MySQL instance. All of that, deployed by a single Terraform file:

Note that the database password is in plain text. Of course you won't do that in the real world (right? right!). Consider using AWS Secrets Manager to store the credentials or use random generated credentials.

The k8s/ file also configure a volume mount for the /wp-content directory. Together with the k8s/ file, the /wp-content directory is being served by AWS Elastic File System (EFS).

Since the cluster is running 3 replicas of WordPress, it's important to persist the data between the instances. AWS EFS is a network file system, thus the same /wp-content is shared by all WordPress instances, like magic. 🤯

Summarizing, WordPress has 3 important parts as mentioned earlier. The first (/wp-includes, etc) comes directly from the WordPress Docker image. The second (/wp-content) is being served by AWS EFS and the last (MySQL Database) from AWS Aurora MySQL.

Accessing the WordPress install

To access your website, change the DNS of the domain used on the file to the ones provided by AWS Route 53:

To speed things up while you're just testing, get the load balancer hostname with kubectl get svc command, discover the IP addresses behind the hostname with dig or nslookup commands and then update your /etc/hosts file to bypass the DNS temporarily.

With the DNS all set up, you can now access the WordPress install!

If you accessed with https://, you will need to ignore the SSL warnings. The installation wizard will only request you to create the super user cause the database credentials was already provided by Terraform. 😎

Using WordPress CLI

Let's extend our knowledge a little bit by installing a WordPress theme, but not from the WordPress dashboard, let's do it from the CLI cause we h-a-c-k-e-r-s! No, we are not, but let's do it anyway. 

Access one of the WordPress instances with kubectl:

$ kubectl exec --stdin --tty $(kubectl get pods | awk '/kube-wp/{print $1}' | tail -n1) -- /bin/bash

There it's possible to install and run the WordPress command line interface. Simple as that:

$ cd /opt/bitnami/wordpress/; curl -O

To install and activate the latest WordPress theme for example:

$ php wp-cli.phar theme install twentytwentyone --activate

It's possible to see the theme being used already by accessing the website:

Cleaning Up

There are a lot more that you can and probably should add to a production environment, such as Let's Encrypt SSL certificate with the help of certbot; WAF to protect against hackers; define the best Kubernetes Autoscaling strategy; automatically update your WordPress pods with or even migrate to a GitOps workflow, just to mention a few.

For now you already successfully deployed a WordPress install using state-of-the-art technology and it's time to clean everything so you don't end up with a enormous AWS bill at the end of the month.

When you're ready to clean all resources deployed, run one last command and everything will be deleted:

$ terraform destroy

And just like that there is nothing left. Infrastructure as code FTW! 💯