Using S3 for static site hosting
I decided to move my blog from sourcehut pages to AWS because I was interested in using Google Analytics (for now) to track page impressions1 and also because I was interested in exploring terraform and the current state of declarative infrastructure. This ended up being considerably more complicated than I expected, but I think my expectations were just incorrect, and the complications are probably warranted. Either way, it can be overwhelming to get such a simple thing set up, and I’d like to document the steps and how they all fit together. So far, everything was configured manually through the AWS console, but I’m going to try to move this to terraform shortly.
The three requirements I had for the new setup were
- use/enforce HTTPS
- provide access to the blog through my top-level fulwiler.me domain
- automatically update the site on push to master branch
To meet these requirements I needed a handful of AWS services.
- S3 to store the actual site (.html files, etc.)
- Route53 to manage DNS for my domain (fulwiler.me)
- AWS Certificate Manager to provision certificates to enable HTTPS
- Cloudfront to put HTTPS in front of the S3 bucket, which do not by themselves provide HTTPS access to static websites
- Identity and Access Management to provision access keys that allow me to publish into S3
Which loosely are related as follows:
After I had created an s3 bucket and configured it to host a static website, I was able to view the site on fulwiler.me. So far so good. Then I tried to reconfigure my DNS so that fulwiler.me would point there. This turned out to be considerably more painful.
The issue is that in order to do so, I needed to convince DNS lookups for fulwiler.me to resolve to my new s3 website endpoint. In DNS, this is known as a CNAME, which basically just points one domain to another. Unfortunately, it’s impossible to CNAME the top-level domain. If my s3 bucket had a static IP address, I would have been able to add an A record to my top-level domain, but s3 doesn’t provide a static IP address. After going down a few other rabbit holes, I realized that the most straightforward approach would be to simply move my domain over to Route53, and use their custom ALIAS records which allow you to point to arbitrary AWS resources. So I transferred my domain out of Google, which in the background is basically just informing ICANN of the canonical owner of the domain, and reconfigured the nameservers to point to a new hosted zone I created in Route53. A hosted zone is a slightly confusing term that seems to just mean DNS configuration, as this is the where you add all your A, MX, CNAME records, etc.
At this point AWS managed fulwiler.me, providing DNS resolution for the domain, and allowed me to point the top level domain to my s3 static website, backed by my new bucket. Other than HTTPS access, I was mostly done.
In order to set up HTTPS, I needed to generate a certificate with ACM, and then add corresponding records to my DNS configuration (through Route53) which prove my ownership of the domain. I couldn’t directly provide my s3 bucket over HTTPS, however, so I needed to set up a cloudfront distribution that simply points to my s3 bucket. After attaching my certificate to my cloudfront distribution2, and then updating my ALIAS record in my DNS configuration to point at my new cloudfront resource (instead of my s3 bucket directly), everything worked!
The last step was simply generating AWS access keys with IAM, storing these in a build secret on builds.sr.ht, adding a new (very simple) app to my flake.nix
apps.aws = {
type = "app";
program = "${pkgs.awscli2}/bin/aws";
};and then building + publishing in my build configuration
tasks:
- build-site: |
cd fulwiler.me
JEKYLL_ENV=production nix run .#jekyll -- build
- publish-site: |
cd fulwiler.me
# only run on master
git diff --exit-code origin/master || complete-build
nix run .#aws -- s3 sync build/ s3://fulwiler.meFiguring out how all of these various s3 resources fit together took a bit of time, and the decoupling of everything at this point feels unnecessarily complex for my (very simple) use case, but I’m sure I’ll appreciate the flexibility as I start to get more familiar with the various services.
-
Sourcehut takes a very principled approach to user tracking, which I appreciate, but seems to also be against any sort of site metrics which feel like two different things. I'd love to move away from Google Analytics to something less invasive, but haven't had the time to look into what the best alternative is.
-
I ran into issues here because it wasn't clear to me that my cloudfront setup needed to establish fulwiler.me as an alternate domain in order to successfully use my HTTPS certificate.