Skip to main content

Blog Infrastructure

As is tradition, I’ve hosted the blog manually using AWS. Last time I created configured an Apache server manually. However, as I grew more knowledgeable about web development, I realized that this was a woefully out-dated solution. So here’s my retelling of how I created this blog.

Hugo

To begin with, I began by using Hugo as my static website generator. I’ve had enough experience with it that using it was relatively painless. There were a few tripping points along the way. The theme documentation referred to a config.yaml while hugo preferred hugo.yaml. As it turns out config.yaml was transitioned away from, and only one of the two should exist. Also, the hugo module needed to be downloaded, otherwise the website silently breaks. This can be done by using hugo mod get. The module also must be referred to by its module name and not its actual name in the hugo.yaml.

Hosting

Previously I had hosted a blog using an Apache server. Fundamentally there’s no issue with that, but renting a server 24/7 is very expensive. Because of that, there are more specialized solutions that have a cost that scales with usage, rather than hogging a server instance 24/7.

Storage

I’ve already had experience with setting up cloud storage with amazon, so this is more of a light skim. Amazon’s storage service is Simple Storage Service, or S3. Objects in S3 are put into buckets, which are basically filesystems. Inside buckets are objects and directories full of objects, which can be downloaded and uploaded using the AWS cli or external tools. I use rclone for S3 upload.

S3 Static Hosting

At first, I found documentation on hosting a server on S3 directly. It required setting bucket permissions to public, which allows anybody with the URL to ‘GetObject’ the contents of the bucket. This works, but suffers from lack of CDN or HTTPS support. For this reason, I found the S3 direct hosting method to be unsuitable.

CloudFront

This leads into CloudFront, AWS’s Content Distribution Network service. For reference, a CDN refers to a set of “edge” servers that holds a cache of your website. When accessing your website, content comes from a server near the client rather than from all the way from the origin server. This has the additional benefit of allowing you to hide your origin, protecting it from DOS attacks.

Cloudfront has “distributions,” each of which cache one service you have. I created a distribution that pointed towards an S3 bucket that contains the built website. It also had to be configured to have the alternate domain name blog.adl-developments.com. A Cloudfront function is also used to redirect people from adl-developments.com to blog.adl-developments.com

Domain Registration

AWS does everything, including domain name registration. Because closed gardens are convenient, AWS makes it very easy to point the registered domain towards cloudfront. AWS calls its service Route 53, and domains are referred to as “hosted zones.” The rules for how traffic is directed are called “records.” I used A alias records to direct adl-developments.com and similar subdomains to the cloudfront service, which can redirect and service as necessary.

SSL Certificate

Of course, in order to use HTTPS we need a certified SSL certificate to verify who we are. Of course, Amazon once again provides the AWS Certificate Manager, who provides certificates signed by Amazon that certifies who we are. ACM is provided free of charge, although the certificates only work with other AWS services. Also, the certificates won’t show any other information other than “signed by Amazon.”

Continuous Integration

Previously, I simply had a script build the website and upload it to S3. That said, I wanted to try out Continuous Integration, which is where after uploading code to a repository, it is automatically built, tested, and deployed.

Github Actions

Github provides a CI service called Github Actions. Actions run, depending on configuration, after a push or pull request. Here, I use some third party libraries in order to build and deploy the website automatically.

The action needs access to AWS and Hugo, so I use aws-actions/configure-aws-credentials and peaceiris/actions-hugo to setup access. Initially I used an AWS user with an access key, which I replaced later. Then I simply ran the shell commands to build the website and copy the public directory to S3. It then tells cloudfront to invalidate and update its cache.

OpenID

This solution however, requires storing a long-term key in Github’s secrets. While not necessarily bad, one can do better. Fortunately, github is an OpenID provider, and can verify your CI pipeline to AWS. To use this, you tell AWS to accept Github as an OpenID Connect identity provider, then create a role with permissions to upload to S3 and create invalidations on CloudFront. You also need to give the CI pipeline access to its own authentication token.

Conclusion

That said, I can say safely that this is the most modern blog I’ve ever developed. Unfortunately it’s mostly constrained to AWS’s walled garden, but I imagine that with enough effort it can be ported to Cloudflare or some other service with relative ease. It also took a very long time, but like most infrastructure should require relatively little maintenance.

TLDR

I put the website on S3, distribute it on CloudFront, then deploy it using Github Actions.