Building a static site with Hugo, Terraform, and AWS


posted | about 8 minutes to read

tags: amazon web services terraform hugo devops tutorial web hosting web development

If you’re a frequent visitor to my blog, you may notice that it has a very new look as of today. This facelift isn’t just cosmetic or just related to any personal disclosures (although I’ll admit those played a role) - I’ve actually changed the framework that the blog runs on from Wordpress to Hugo as part of my exploration of serverless computing and web hosting. Hugo’s a static site generator, which means that instead of having a database and a comparatively slow (and potentially vulnerable to security exploits) Wordpress install hosting my site, I just have a bunch of raw HTML pages. You might think this sounds much less maintainable, but you might be surprised - it’s actually very easy! I simply write my blog posts in Markdown and Hugo compiles it all into static pages for me automatically. Hugo has a good initial tutorial as well as some great documentation to help you get started on their end, but today I want to take you through moving your existing Hugo site up into the AWS cloud in a maintainable, easy fashion.

Before we get started actually putting everything up, I want to start by explaining my philosophy in terms of how I designed the hosting stack. The idea was basically to minimize costs while also preserving ease of use for the website developer. To that end, I put together a list of expectations, and identified AWS technologies that could help me achieve those expectations:

All of these were pretty straightforward to identify, but I didn’t want to have to spin them up individually or redo the entire stack by hand if I decided to build another Hugo site in the future. I played around with CloudFormation for a while, but found it a bit too clunky to really use effectively (although this is probably personal preference). Instead, I went with Terraform, which is a third-party offering that works with multiple cloud platforms. I found the syntax elegant and easy to understand, and was able to define my entire technology stack in less than a day. I’m sharing that Terraform template with an accompanying Lambda function and AWS CodeBuild buildspec.yml here - they’re the exact same files I used to spin up this site.

Terraform and Infrastructure As Code

If you’re at all familiar with AWS, I think you’ll find really easy to grasp. The general idea is that you’re actually building out your AWS infrastructure block by block, adding configuration options for each of the things you want to build. This means that your infrastructure is much more maintainable; instead of having to use the AWS tag system or Visio charts or a spreadsheet or whatever to keep track of your AWS assets, you can have a concise code-based document that explains exactly what your infrastructure looks like, and by updating that document and re-applying, you can make sure that your infrastructure always matches what’s documented in the Terraform file. Terraform will also take care of actually spinning up each individual piece of the stack while also being aware of dependencies: for example, it’ll wait until the S3 bucket is created before spinning up the CloudFront distribution. It’s a really neat tool that works extremely well and can save you a lot of work in the long run. I’m going to go through the file that I shared very briefly, then discuss how to spin up the stack.

Starting at the top of the Terraform template I provided above, you’ll notice some variable blocks. These blocks will prompt you to enter values at the beginning of the setup, or default to their listed default value if you don’t provide values to go with them. Pretty self explanatory, but extremely important. Take special note of the domain variable as we’ll be revisiting that one a lot as we go through the file.

Make sure you have AWS API keys set up before moving forward - Terraform will ask for them later!

After the variable blocks, we briefly set up the AWS provider, which just tells Terraform that we’re going to be building on AWS. Nothing spectacular here. Then we move on to a data block which is referencing a CodeCommit repository. We actually need to go create this repository. We could set it up to be part of the build, but we very much do not want to do this because if we do, it’ll be managed by the Terraform file. That means if we ever want to spin down the infrastructure later, we’d also be deleting the Git repository. We don’t want to do that, because we may want to spin the site back up in the future, and we don’t want to lose our site files. The best way to do that is to create the repository outside of Terraform. You’ll notice in the script that the repository name needs to be ${var.domain}, which refers to the domain variable that we saw at the beginning of the file - so go ahead and go into CodeCommit and create that repository now.

Once you’ve got that done, we can move on to the rest of the Terraform file. The rest of the stuff you’ll see is stuff that Terraform will be creating on its own - mostly resource blocks, but also some data blocks that contain IAM policies that we apply as part of other resources. I won’t go through all of that - I’m assuming you have some basic familiarity with AWS technologies - but feel free to tweak anything in here according to your needs. In short, the template as written executes the following actions:

Make sure that your domain is pointed to Route 53 nameservers, or else the Terraform template will get stuck on requesting a certificate.

Spinning Up The Stack

It’s pretty easy to actually use Terraform. You’ll need to download and install it per the documentation, then hop into the directory where you have your Terraform template downloaded and run terraform init to get it all set up. terraform plan will give you an outline of the changes that it’ll make, then you can run terraform apply to actually run those changes. Once it’s complete, you’ll have your entire stack built out - and all you have to do is commit your Hugo files to your CodeCommit repo. (Make sure you add the provided buildspec.yml file to the root of your site so that CodeBuild will know what to do with your files. Obviously, if you’re using another static site generator like Jekyll or something, you can modify the buildspec to fit your needs.)

If everything works, you should be able to view your site live at the domain you set it up on immediately after committing your files to the Git repository - and every time you commit again (say, to add a new blog post), your site will update on the fly. If you ever want to spin down your hosting infrastructure, just run terraform destroy and Terraform will ask you if you’re sure before tearing down all of the managed infrastructure. It will leave your CodeCommit repository in place, meaning that spinning your site back up is as easy as just running terraform apply again.

This idea of being able to spin up and spin down the stacks at will is extremely convenient when you have multiple sites to manage. All you have to do is run terraform apply or terraform destroy with the proper input variables, and Terraform can automatically spin up or spin down whatever domain you want. This should make managing multiple websites a breeze!

Next Steps

There are a few things left. Spinning up a static site is all well and good, but it’s still just a static site at the end of the day. In a future post, I’ll discuss leveraging Lambda and the API Gateway service, along with some other AWS services, to add dynamic content like blog comments or a contact form to your static site within a serverless design. To do this, I’ll extend the existing Terraform template so that the whole thing remains easily maintainable.

I think that’s about it as far as spinning up a Hugo static site’s concerned, though. Let me know if you have any questions by reaching out on my contact page.