DEV Community

loading...
Cover image for Serverless Static Wordpress on AWS for $0.01 a day
AWS Community Builders

Serverless Static Wordpress on AWS for $0.01 a day

Pete Wilcock
AWS Community Builder
Originally published at techtospeech.com Updated on ・8 min read

You’ll think this article is clickbait, but it’s not. I’ve built a fully-functional static serverless Wordpress solution on AWS, with Global CDN, WAF and A-Grade SSL for literally one cent per day. It’s fast, resilient, scalable, and unlike many Wordpress sites, not susceptible to brute-force login attacks. 

What’s more: You can do it too. It’s wrapped up in an open-source Terraform module and I’m kind of hoping it’ll break the internet with its accessible simplicity. You can set this up from a standing start in less than 30 minutes.

What does Serverless Static Wordpress do?

Serverless Static Wordpress is a Community Terraform Module from TechToSpeech that needs nothing more than a registered domain name with its DNS pointed at AWS. 

It creates a complete infrastructure framework that allows you to launch a temporary, transient Wordpress container. You then log in and customise it like any Wordpress site, and finally publish it as a static site fronted by a global CloudFront CDN and S3 Origin. When you’re done you shut down the Wordpress container and it costs you almost nothing. 

The emphasis is on extremely minimal configuration as the majority of everything you’d need is pre-installed and pre-configured in line with industry best practices and highly efficient running costs.

Have an existing site that you'd like to convert to Serverless Static Wordpress? No problem. 

There’s a lot to get through here so this story is broken up into two different articles; The Short Version and The Long Version. Pick the first one if you want to grab this thing and get started immediately, and the second one if you want to know exactly how this solution works under the hood and learn about all of the work it took to put it together. 

If you are familiar with AWS this will go a lot faster, but even if this is your first time The Short Version steps should get you up and running in no time.

Alt Text

The Short Version

For all of these steps we’ll use the example domain www.peter.cloud.

Step 0 - Create an AWS account.

Step 1 - The domain

In your AWS account, create a Route53 Hosted Zone for your domain name. Once created, this will display four DNS nameservers for your new website.

Go to wherever your domain name is registered (it could be AWS itself, or anywhere else), and update the DNS Servers to the four just created.

Step 2 - The tools and credentials

Ensure you have Terraform, AWS-CLI (with a user and credentials) and optionally (but preferably) Docker with the service started.

Step 3 - The code and the plan

Check the GitHub README for the project for extensive code examples to get set up and deploying quickly.

The module does _almost_ everything by itself, but there are a couple of extra resources specified here that’ll make it a completely hands-off end-to-end process. These are the parts that need docker installed and the AWS CLI configured properly with a default region and profile.

Terraform init, plan, and apply, then sit back and get a drink. The full end-to-end creation process here should take about 10 minutes. Unless you did something wrong, this should complete without errors - but just in case it does try running the plan and apply steps again.

Step 4 - Launch Wordpress

What you did in Step 3 is create everything you need to launch Wordpress and publish your static site - but it’s not launched yet. If you didn’t add the optional Terraform resource to trigger the CodeBuild job, you’ll need to head to the CodeBuild console in your chosen region and start the ‘-serverless-wordpress-docker-build’ job and wait for it to complete (it’ll take about 2 minutes). 

Modify the ‘launch’ attribute of your module from 0 to 1, and then run ‘terraform apply’ again. Wordpress will now launch. The first-time set-up will take roughly 5 minutes while the initial site is created, configured, and the necessary plugins are installed. 

By default, your Wordpress installation will become available at http://wordpress.yourdomain.com (Why no SSL for this part? Check The Long Version for details)

The default username is ‘supervisor’ and default password is ‘techtospeech.com’. You can either override these, along with the default subdomain, in the module’s configuration, or modify them after you log into Wordpress. Please make sure you do!

Step 5 - Post and Publish

This is now a Wordpress installation like any other. You can install most plugins (although any with explicit server-side functionality won’t work in a static site), any themes you like, and even import an existing site backup. SEO plugins like Yoast work just fine. If the site is idle for more than 5 minutes the backend database will pause (to save money), but simply refreshing the admin console will make it start up again after a few seconds of delay.

Whenever you want to stop the container, toggle the ‘launch’ attribute of your Terraform configuration back to 0 and re-run ‘terraform apply’. You can also manually scale the ECS Service to 0 tasks in the AWS console. A better method of one-tap launching and stopping the site will be coming in future! Stopping the container doesn’t lose any data - the database will be saved in the background, along with any files, images, plugins or themes you installed. Its state is perfectly preserved until the next time you launch it. 

But - it is critically important that you remember to shut the container down when you’re done. Otherwise it’ll keep on running - it won’t cost you too much if it does but the whole point of this solution is to save money, not waste it! (Another future update will have an auto-shutdown-when-idle feature)

When you’re ready it’s time to publish. Check out the WP2Static plugin that has been automatically installed for you, along with the WP2Static S3 Addon. Leon Stafford is the creator of these plugins. He’s the open-source static Wordpress guru without whom this project would not have a deployment mechanism. I talk more about Leon in The Long Version

You can check out all of the WP2Static options you can tweak to configure your deployment, but the auto-setup has pre-filled everything you need to publish. Hit the ‘Generate Static Site’ button and periodically refresh the log to check on status. This will now crawl and rewrite every URL to a neat static version that then gets pushed directly into the S3 bucket backing the static version of the site. 

This process can be as fast as 3-5 minutes or much longer if you have a larger site. We’re working on faster methods for these different steps (and help is gratefully received!) but at the moment the version bundled with this set-up is stable and reliable with a little patience. 

Once the process completes…. You’re done! Head to the https version of your site (in this example, https://www.peter.cloud) and be amazed at how your website looks identical to how it did before, except now it’s serverless, static, cached with a Global CDN and won’t go down to some feeble DDoS probing or wp-login brute-forcing. In fact, try to go to https://www.peter.cloud/wp-admin - ha! Doesn’t exist!

Is it really only $0.01 a day?

Yes, but also no. It depends. 

Consider the typical use case. You’re a nerdy tech person with a personal blogging site (and I am, check out petewilcock.com), you’ve got a few articles, you probably get between 50 and 200 hits a day. With that kind of traffic the site will run in its static form without any intervention for around $0.01 a day - completely legitimately.

On the other hand, if you’re a globally-popular massive content-generation business with thousands of articles and millions of hits a month… it’ll cost more. Your only variable cost with traffic is CloudFront distribution charges, and you can mitigate these by either getting a discount with a CloudFront Security Savings Bundle, or if you’re a particularly big player you can enquire about custom CloudFront pricing directly to AWS. WAF has some unavoidable fixed costs that completely falsify my clickbait title and will add at least $0.60 a day to running costs. But if you're suspeptible enough to need the WAF, I'm guessing you can afford it. 

As I run several static Wordpress sites using this set-up, I’ve paid literally $1 a month for a Savings Bundle and that covers a lot of my usage. Previously I was running a T3 web hosting server with CPanel licencing that cost around $600 a year. Now it’s not a strictly fair comparison for a few reasons (this set-up doesn’t handle email at the moment for example - but look out for a future update!), but you start to get an idea of the difference between this and a ‘conventional’ hosting set-up for a basic website. Even other providers of ‘Static’ Wordpress sites can’t compete with this, because they need a profit-margin and you don’t. 

The only other costs to be aware of are the ECS Fargate container running costs that backs Wordpress (and it runs in Spot mode so it’s very cheap), and the RDS Aurora Serverless v1 database that backs the Wordpress database. This is actually the most expensive bit (and you guessed it, a future version will offer a cheaper alternative if you want to sacrifice the features and convenience), but you’re only charged for the time you’re actively modifying the site. As an example, if I’m editing the site for several hours, this might cost around $0.30. The key part is that the vast majority of the time when you’re not adding content, it costs practically nothing.

Troubleshooting

No set-up is ever perfect, so if you have any issues with the Terraform module please report them on our GitHub and we’ll take a look. Contributions are also welcome!

If you experience an issue with the WP2Static plugin, check out their issues page for common issues and solutions and the chances are anything you encounter can be tweaked to work as you’d like. You will be free to upgrade the plugin, or Wordpress itself, whenever you like - but always take a backup first! I recommend UpdraftPlus as a great free Wordpress backup plugin.

In Conclusion

Firstly another reminder. Don’t forget to turn off your Wordpress container when you’re done!

This module is the culmination of many months of painful iterations both to get it working for myself, and then extensively bashing it into shape so I could publish it publicly without embarrassing myself. It leverages all of my experience of AWS since I started with it back in 2014 and compresses every element of DevOps and web development I’ve ever learned into a tight solution that can work for anybody. I hope it works for you!

To learn more about this mad journey, check out The Long Version article.

-- Pete Wilcock is a 9x AWS Certified DevOps Consultant, AWS Community Builder, and Technical Writer for TechToSpeech. If I’m not possibly losing my mind and definitely my social life buried in some project, you can find me on LinkedIn, Twitter, GitHub, or my personal site.

Discussion (45)

Collapse
rehanvdm profile image
Rehan van der Merwe

Nice, I wrote something similar a few year back rehanvdm.com/serverless/serverless....

Except back then I wasn't big into IaC. The main difference is that I have the whole WordPress setup on an EC2 instance. Then just start it when I am writing a blog, then exporting the static content, also using WP2Static and then shut it down again.

Then I deploy locally by extracting the static zip and running a few Gulp scripts to do post processing like compressing images, and a few HTML and IP replacements. Then I just do an S3 sync.

Collapse
petewilcock profile image
Pete Wilcock Author

It's really interesting @rehanvdm because if the plugin (or a fork) could be customised for true AWS-native deployment, the publish step could be incredibly fast and scalable, and not rely on running within the Wordpress installation at all. I've discussed this a bit with @leonstafford and I really like the idea - I just don't have enough (any) experience in building Wordpress plugins so I can't do this myself!

Collapse
rehanvdm profile image
Rehan van der Merwe • Edited

I have limited experience with WP Plugins, just don't have capacity atm. If I were to do it over again now, I will defintley not use Wordpress at all. Back then I didn't know how/what is involved in running your own blog. Things like OpenGraph tags, social media sharing buttons, how to do "recomendation" blogs based on current, SEO related configs.

*When I am doing the next face-lift change I will defintley move to a Vue + Markdown type of solution. The image compressing is also an extremly CPU intensive task that runs on my local machine, so I will also move that to some CI/CD pipeline running on the cloud.

Glad to see the Plugin is still active, great write up!

Thread Thread
petewilcock profile image
Pete Wilcock Author

I'm using Smush for image-optimization which is pretty good, although sadly S3 doesn't appear to understand the mime type of WebP by default, and the S3 addon for WP2Static doesn't (yet) have an option to specify your own metadata based on file type. Still many, many optimizations left to exploit :)

Thread Thread
rehanvdm profile image
Rehan van der Merwe

Thanks for the mention, haven't really looked at doing image optimization on WP, might transition to that. Currently using npmjs.com/package/gulp-imagemin.

I will soon do something similar to this medium.com/nona-web/converting-ima... to do optimization on the "fly" for one of the internal company projects.

Thread Thread
petewilcock profile image
Pete Wilcock Author

Oh now you've got me thinking about a Lambda@Edge integration to rewrite any jpg/png requests to a generated WebP version, I could even fix the metadata in the response to guarantee it works! adds to list

Thread Thread
rehanvdm profile image
Rehan van der Merwe

That's exactly the plan :) Also if you just want to change metadata/headers look into the newly released CloudFront Functions, those are less expensive and a perfect use case for header rewrites etc.

Collapse
kevinhooke profile image
Kevin Hooke

I've gone through multiple iterations of my personal site over the past 19 years and found there isn't any cheaper option to run a PHP based Wordpress than the cheapest VPS you can find for a couple of dollars a month. This static site approach is interesting, I'm going to take a closer look. However, seeing WAF in your diagram and knowing 1x WAF ACL is $5/month and 1x WAF Rule is $1/month, you already lost me. Reading through your longer article and seeing "WAF has fixed costs which completely break the ‘$0.01 a day’ clickbait" , yeah, you got me, I fell for your clickbait.

Collapse
petewilcock profile image
Pete Wilcock Author

Sorry @kevinhooke - there's only so much you can optimize and if your use case requires a WAF (it's optional), there's no clever way around it aside from mitigating costs with a CloudFront Security Savings bundle. Consider though that there's not that much a WAF offers for a static site except perhaps DDoS mitigation from known bad IPs.

Hand on heart, one of my sites with this set-up (minus WAF) does indeed cost $0.01 a day.

Collapse
kevinhooke profile image
Kevin Hooke

I'm still interested in the approach, so will still be taking a look. $0.01 is still less than my current $4/month VPS, although not by that much.

Collapse
leonstafford profile image
Leon Stafford

Haha, I feel your pain! I've looked several times at how to offer free or really cheap hosting to people. Best I can do today is lokl.dev - as you mentioned it's a personal site, it may fit your workflow OK. Else, would love to hear why not, maybe can adjust it.

Collapse
aviboy2006 profile image
Avinash Dalvi

Detailed blog. 👍🏻

Collapse
phlash909 profile image
Phil Ashby

I'm currently looking to migrate a few (<10) Wordpress sites off my VM in Azure to static hosting, so I can turn off the VM entirely and save ~$20/mnth. My plan presently is to use Lokl to host each site instance in a container (thus not polluting my local machine with WP stuff), and export static pages via the wordpress.org/plugins/export-wp-pa... plug-in. Testing so far indicates this is workable, and I will be inflicting it on the nice person who owns the sites to run on their Linux PC, leaving me with only cloud storage/CDN costs :)

Future plans involve dumping WP entirely and moving to Hugo but this first step provides the cost savings for me!

Collapse
petewilcock profile image
Pete Wilcock Author • Edited

Good stuff @phlash909 - Lokl is another tool by Leon Stafford - the guy that made WP2Static bundled here. The only sticky bit in the plugin you mentioned is that terribly manual step where you have to 'download html files' and then do something useful with them! This is why I regarded a native S3 integration to be so important for this one. I want to do almost nothing to make it publish!

Collapse
phlash909 profile image
Phil Ashby

True, publishing (in my case to Azure storage) might need me to write some glue scripts and poke the WP API, unless anyone knows of a WP->Static plug-in that supports Azure?

Thread Thread
leonstafford profile image
Leon Stafford

Hi Phil,

Great to hear of Lokl being used!

My next plans (after a little Xdebug profiling support) for Lokl, will be adding wizard options to easily setup and deploy sites to Azure/GAE/Cloudflare, etc, using their CLI tools. These tend to have better performance and saves me having to write/maintain custom code to work with their APIs.

Feel free to message me more about the Azure needs - the more I hear about it, the more front of mind it is for me to work on :)

Collapse
fededalla profile image
Federico Dalla F • Edited

I'm always getting a "standard_init_linux.go:219: exec user process caused: no such file or directory" when trying to launch the ECS task.

I had to run docker push and code build manually.

Do you know how can I fix it?

Task configuration: dev-to-uploads.s3.amazonaws.com/up...

Collapse
rkosolapov profile image
Ruslan Kosolapov

Hi Federico!

I'm researching the problems people faced with AWS and I'd like to have a short talk to you about your case (I suppose you've tried this solution because you have smth in your mind about static WP or AWS automation).

PS: I'm new here and I didn't find any personal messages, so, sorry for off-topic.

Collapse
fededalla profile image
Federico Dalla F

Hey Ruslan! I have added you to linkedin, let's coordinate there.

Collapse
petewilcock profile image
Pete Wilcock Author

Can you confirm the code build job completed successfully? The initial push of the docker base image and triggering the build are the two manual steps if you don't use the helper examples.

Collapse
fededalla profile image
Federico Dalla F

The code build job completed successfuly on the first try and generated the "wordpress_docker.zip" (3.8 MB) in the Build S3 bucket.

The command ran was:
aws codebuild start-build --project-name viajesconanto-serverless-wordpress-docker-build --profile default --region us-east-2

Thread Thread
petewilcock profile image
Pete Wilcock Author

So the wordpress_docker.zip merely contains the assets for the build job - it uses this to build the container and push it into ECR within the same region. You should see two containers there, one tagged base (which is the uncustomized docker image) and one tagged latest, which is the result of the CodeBuild job. With this present you should be able to launch the Wordpress container.

If you are still experiencing issues, please feel free to raise an issue on GitHub with some more information about your set-up and I can investigate.

Thread Thread
fededalla profile image
Federico Dalla F • Edited

Yeah, I'm seeing both. The base one was generated about 5 minutes before than the latest one: dev-to-uploads.s3.amazonaws.com/up...

Also I am was able to verify that the task is running the latest image (241712483418.dkr.ecr.us-east-2.amazonaws.com/viajesconanto-serverless-wordpress:latest).

So my guess is that the image is throwing the "no such file or directory" error.

I will report this problem (major one that does not allow me to continue). Also I have find minor other ones, like:

  • The aws route53domains command lacks of two spaces (typo) between each of the Name=${} definitions.
  • The password override defined for the RDS Aurora server contains the illegal "@".
  • The terraform module only can be executed in regions that support RDS Aurora Server with Serverless option provisioned. I tried to launch it in South America (Sao Pablo) wich lacks of it.
  • The docker push command when running from windows lacks of double quotes escaping so when ".terraform" path tried to be executed the cli understands is trying to run that executable.
Thread Thread
petewilcock profile image
Pete Wilcock Author

Thank you, your feedback to track as an issue would be welcomed!

Collapse
ojacques profile image
Olivier Jacques

Thanks for sharing! I just recently moved my (small!) 3 WP sites hosting from a small Azure VM (leveraging AIO WP+SSL+Nginx reverse proxy thanks to github.com/selloween/docker-multi-...) back to my NAS (Synology) on Docker to save cost.
I looked at static WP before, but my sites were not looking exactly the same. I have not tried WP2Static, which may be the way to go!
Going forward, I will look at keeping the wp-admin piece on my NAS, and moving the site to S3/CDN.

Collapse
justynclark profile image
Justyn Clark

I already do something like this for my Gatsby static sites. I left WP alone years ago after developing with it for 9 years on GoDaddy services.

I'd definitely try a setup like this if ever touching WP again.

Collapse
dylandelobel profile image
Dylan Delobel

I would choose Jekyll, Hugo or hexo for a tech personal blog,
If one day I need a WP I will try your solution! Bookmarked!

Collapse
capdragon profile image
CaptDragon • Edited

This is great so thank you for this. I'm assuming this won't work for any sites containing sign-up forms or with comments enabled correct? Being serverless, no forms would work correct?

Collapse
ojacques profile image
Olivier Jacques

If you need comments, and your target audience are devs (well, GitHub users), then I can only recommend giscus, which leverages GitHub discussions for comments: giscus.vercel.app/

Here is how it looks: techdebtburndown.com/episode8/

Collapse
petewilcock profile image
Pete Wilcock Author

@capdragon Indeed not at the moment, although I recognise a form mailer will be essential for many. I'm looking to integrate something like this: github.com/DJAndries/terraform-aws... along with a bundled plugin that'll let you formpost as you usually expect.

For my own sites, I'm using a Facebook comment plugin, but again I think I could rustle up an acceptable alternative to this in time.

Collapse
leonstafford profile image
Leon Stafford

@capdragon , here's some options, there are more recent ones which also look good, not yet listed there:

tnd.dev/?tnd-dev%5BrefinementList%...

Collapse
dmk1111 profile image
dmk1111

Great work!

Collapse
reducedhackers profile image
nicholas butler

Ive tried to read the longer version but it keeps redirecting to what appears to be a spelling error

the https reads as httsp

One question I have though is .. how to stand-up the container when you want to make edits and then republish

I think I missed/skipped that logic

Collapse
petewilcock profile image
Pete Wilcock Author

Sorry about that, I've fixed the typo. The module repo has all the instructions, but there is a 'launch' attribute to the module that you toggle to 1, then plan/apply, this launches the container. When you're done, you set it to 0 and plan/apply again.

Collapse
jolo profile image
JoLo

Very very good article.
I wonder if you pay for the database with this setup?

Collapse
petewilcock profile image
Pete Wilcock Author

Yes you do, but it's serverless so only for the time that it is in use to make edits and publish.

Collapse
jolo profile image
JoLo

Thanks Pete for your reply.
Will the changes happen on the fly or do we have to uncache cloud front first?

Thread Thread
petewilcock profile image
Pete Wilcock Author

The CloudFront cache settings will be respected, so depending on what you set in the cache-control:expiry header as in the S3-Addon, the results will continue to be cached in CloudFront for that amount of time. The default set on the distribution is 7 days if there are no other values.

You can speed this up with a CloudFront invalidation, but that must be used with caution.

Thread Thread
jolo profile image
JoLo

Got it. Thank you so much :)

Collapse
wordskill profile image
donnacha

Well done with this Pete, looking forward to trying it.

URL typo, third mention of the long version (in the paragraph mentioning Leon Stafford), you typed https:// as httsp//

Collapse
arki7n profile image
Akhilesh Yadav

Its breaking here:


│ Error: error creating public access block policy for S3 bucket (tech.ayzom.com): OperationAborted: A conflicting conditional operation is currently in progress against this resource. Please try again.
│ status code: 409, request id: TEQBA7CHAF3XPEGD, host id: P6nl4kcOmTs6L0fmqN9zzMXBtv8Dr2k63HZWaUoaPOwk7B3sms0XKhPeGK9KYf9NLsrYvkdZol4=

│ with module.cloudfront.aws_s3_bucket_public_access_block.wordpress_bucket,
│ on modules\cloudfront\distribution.tf line 16, in resource "aws_s3_bucket_public_access_block" "wordpress_bucket":
│ 16: resource "aws_s3_bucket_public_access_block" "wordpress_bucket" {



│ Error: Creating CloudWatch Log Group failed: OperationAbortedException: A conflicting operation is currently in progress against this resource. Please try again. '/aws/lambda/us-east-1.ayzom_redirect_index_html'

│ with module.cloudfront.aws_cloudwatch_log_group.object_redirect_ue1,
│ on modules\cloudfront\main.tf line 21, in resource "aws_cloudwatch_log_group" "object_redirect_ue1":
│ 21: resource "aws_cloudwatch_log_group" "object_redirect_ue1" {

Collapse
petewilcock profile image
Pete Wilcock Author

This looks related to AWS trying to recreate resources that have just been deleted: aws.amazon.com/premiumsupport/know...

I would say wait a few moments and try again to resolve.

Collapse
mishi profile image
Mishi

Thank you for sharing this Pete, this was very useful! Can't wait for the future improvements. 😀

Collapse
earlgeorge profile image
გიორგი George Dav

wonderful

Some comments have been hidden by the post's author - find out more