This site runs on AWS, using CloudFront, ECS, RDS and EFS (among other services). Here’s how I built it.

This site is on WordPress – as much as this isn’t ideal anymore, until I come to completely rebuild it and all the other sites I run, it’ll have to do. As such, I wanted to switch it to a highly available setup in AWS (previously it was running on a single server hosting both Apache and MySQL with no automated backups [shudder]).

This is all deployed via Terraform and GitHub Actions.

The configuration is stored in GitHub.


Here were my requirements:

  • Cheap (ish)
  • Secure
  • Highly available (ish)
  • Automated
  • Running WordPress Multisite (Network)

I chose to go with the WordPress Docker container running on ECS, using EFS for file storage and RDS for the database. I chose to use Fargate for ECS so there’s no need to manage the hosts. All of this sits behind an Application Load Balancer, which is fronted by a CloudFront Distribution and a WAF is attached to that. DNS is managed by Route53.


Running ECS gave several advantages. Firstly, it’s always best to use the mantra of “cattle not pets” and ECS is designed to run ephemeral tasks. The community-maintained WordPress container meant I didn’t have to bother too much with building and maintaining my own WordPress instance or container, and finally (big win) the use of Fargate to alleviate the to maintain my own container instance(s).

To keep it cheap, I only run one task. I could run two but I’m honestly not that fussed if my website goes down for a bit when the alternative is a higher cost of maintaining multiple tasks. The Application Load Balancer performs a HTTP Health Check, looking for a HTTP 200 code from WordPress. ECS will replace the task if that health-check fails.


CloudFront is used to cache content. There’s a different CloudFront distribution for each website, with the origin being the load balancer. There’s a random string, generated by Terraform, that CloudFront passes as a header and the Load Balancer checks to ensure it has the correct value. This is a fairly crude way to ensure that only requests originating from CloudFront are permitted to reach the back-end servers. Terraform also uses the published IP ranges for CloudFront to create Security Group Rules.

There are different CloudFront behaviours set up for special WordPress paths. I used this blog as guidance on setting up the CloudFront behaviours for various WordPress functionalities.


To deploy, I use GitHub Actions. This is the simplest, easiest way to deploy configurations directly from GitHub.

There are two GitHub action:

  1. On Pull Request – when a new PR is created into main, an action is run to perform Terraform validation, formatting and security checks (using tfsec). It then runs a Terraform Plan and puts this as a comment on the PR. This is really useful (an example screenshot is shown below)
  2. On Merge – when a PR has been merged, the GitHub actions deploys automatically.

All the runners use on-demand EC2 runners from this GitHub Action.