A while back I shut down my blog. It was a simple wordpress site hosted using one of the free marketplace AMIs on EC2. It was a terribly ineffecient way to host ($12.50/month), but I did it and it worked. A friend and past coworker of mine, hammackj, mentioned a while back that he used Jekyll for building his blog as a static site; This was around the time my wife and I had our first kid, and between that and the everyday challenges of life I haven’t had time to test it out. But now I do.

This post details how I:

Getting Started with Jekyll

There’s a reason Jekyll is the most popular static site generator. Created by GitHub co-founder, Tom Preston-Werner, it’s the technology that backs GitHub pages. Jekyll has a very mature community, with fantastic documentation, tutorials and free resources (themes, layouts, styles) to help you get started. I followed their Quickstart guide, and was up and running within minutes.

By default, it uses the minima theme, which comes with a lot of helpful _layouts and _includes. To make changes to the theme, find the source files in the gem (bundle show minima) and copy the files you want to edit into the appropriate folders within your project, as detailed here. I changed the home layout to include excerpts of recent blogs, as well as removing some extra information from the footer. The full Jekyll documentation can be found here

You can run your site locally with bundle exec jekyll serve. Changes are hot-loaded, so by refreshing the page you can get a live look at what you’re actually writing. Once you have something you’re happy with, it’s time to get it onto to world wide web where you can share it with others.

Configuring an S3 bucket for webhosting

I went with an AWS oriented solution because:

  • It’s cheap (S3 storage costs $0.023 per GB)
  • I already know it

AWS can be daunting with all of its vaguely named service offerings, and if misconfigured can give you quite a surprise when your bill comes at the end of the month. I’ve been using AWS extensively at work and at home for the past 3 years, and once you know what the services actually do setting up a static site is a pretty straightforward task. If you don’t already have an account, go ahead and create one here. I would recommend not using an email that’s already tied to an Amazon account; the two are inseparable (ask me how I know), and will share a password as well as MFA.

Creating the bucket is very straightforward. Log into the AWS Console and go to the S3 page. Clicking the ‘Create Bucket’ button will yield the following form:

S3 Create Bucket Form

Bucket name must be globally unique across all accounts. Luckily nobody had taken the FQDN of my blog! For region, select whichever one gives you the lowest latency from wherever you live. CloudPing is a good tool to help figure this out. CloudFront will serve as your CDN, and will handle replicating your site across as many servers as you want to pay for, so your readers will have low latency as well.

S3 Create Bucket Form

I strongly recommend adding a unified billing tag to your bucket if you ever want to see how much your blog specifically costs. The most confusing part of the AWS ecosystem is the billing. It’s really good if you want to see your spend by service (EC2, S3, ECS), but as soon as you try to stretch across services it becomes a nightmare. I spent a few long nights going through 30 page PDF printouts with a highlighter and pen and coordinating it with the AWS Console to track down our companies $25k/month spend in our legacy infrastructure. It is not fun. We’ve since migrated to a completely new account where everything is defined in CloudFormation templates that automatically apply billing tags, so at the end of the month it’s easy to see if our money is going to the right things.

S3 Create Bucket Form

You’ll want to uncheck “Block all public access”. This won’t make your bucket public, but leaving it checked will prevent you from making it public in the future. This will eventually be a public bucket, since it’s where your website is actually coming from. Verify your settings on the final summary screen, and you’re done!

Push to s3

While you’re still in the AWS Console, we’ll need to set up a service account for pushing your site to S3. Go ahead and open up IAM, and navigate to ‘Policies’. The easy thing to do would be to use the ‘S3 Full Access’ policy, as this gives a user full access to all S3 operations. The downside to this is your service account can now access any other buckets you have, as well as create and delete all buckets. We want to limit the scope of our account’s access, so let’s create a new policy with the following JSON policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:DeleteObjectTagging",
                "s3:ListBucketByTags",
                "s3:ListBucketMultipartUploads",
                "s3:GetObjectVersionTagging",
                "s3:ListBucketVersions",
                "s3:ReplicateTags",
                "s3:PutObjectVersionTagging",
                "s3:ListBucket",
                "s3:DeleteObjectVersionTagging",
                "s3:ListMultipartUploadParts",
                "s3:PutObject",
                "s3:GetObject",
                "s3:GetObjectTorrent",
                "s3:AbortMultipartUpload",
                "s3:PutBucketTagging",
                "s3:GetObjectTagging",
                "s3:PutObjectTagging",
                "s3:DeleteObject",
                "s3:GetBucketLocation",
                "s3:GetObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::blog.lnicho.com/*",
                "arn:aws:s3:::blog.lnicho.com"
            ]
        }
    ]
}

I called mine BlogUpdaterPolicy. Now create a new User in IAM with Programattic access, and your BlogUpdaterPolicy attached. Make sure to download the Access/Secret key it generates on account creation; you’ll need those shortly.

IAM Settings Example

Actually deploying to S3 is very simple, thanks to the s3_deploy gem. Install it with gem install s3_website. After configuring the AWS CLI and adding a named profile for your blog update service account, you just need to add a s3_website.yml configuration to your Jekyll project. Mine contains:

# Which bucket to deploy your blog to
s3_bucket: blog.lnicho.com

# The named profile from your AWS credentials
profile: blog

After that, you should be able to run s3_website deploy and your static files should now show up in S3!

Creating an SSL Cert with Certificate Manager

If you dig deeper into the s3_website gem it can do the next step for you. I prefer to provision my own AWS resources to make sure they’re properly configured. This step will vary based on who your domain is registered with. I happen to use Google Domains, as you’ll see in the examples. Another thing Amazon does, which is nice, is they provide SSL certs for free to use with their services. If you want your blog to use HTTPS, then follow these steps; otherwise you can jump to Configuring Cloudfront.

Go back to the AWS Console, open Certificate Manager, and Request a Certificate. This is a pretty straightforward process on both sides:

You enter in the domains you would like a cert for

AWS Certificate Manager

AWS asks you to add what equates to a keypair as a CNAME record with your domain registrar

AWS Certificate Manager

You add the CNAME record with your domain registrar (as mentioned I use Google Domains)

Google Domains

AWS verifies the CNAME record against your domain registrar, and…

AWS Certificate Manager

You get a cert! And as long as you leave the CNAME record in place with your domain registrar, AWS will regularly rotate the cert for you before it expires.

Configuring CloudFront

Now go to Cloud Front, and create a new Web distribution.

CloudFront Distribution Settings

Select your bucket from the list in the first dropdown. Origin path is only used if your content is in a subdirectory of the bucket instead of the root directory. Origin ID is just a name field, for your benefit. Feel free to add any custom origin headers if you have a use for them.

CloudFront Distribution Settings

If you have already created a cert with Certificate Manager, you will probably want to change Viewer Protocol Policy to ‘Redirect HTTP to HTTPS’

CloudFront Distribution Settings

For ‘Price Class’ you’ll have to look at the rate sheet and see what you’re willing to spend for distribution. I’m fine with just US, CA, and Europe; this should give reasonably fast access speeds around the globe. ‘Alternate Domain Names’ is where you’ll put your own custom domain name.

CloudFront Distribution Settings

If you did add the aforementioned cert, this is where you wire it up. You just need to choose ‘Custom SSL Certificate’ and choose the one you created from the dropdown.

CloudFront Distribution Settings

Now you just need to add index.html as your root object for the site. It should take about 15 minutes for cloudfront to successfully set up your distribution. The last step is to go to the distribution in the console and find the ‘Domain Name’ that’s assigned to your distribution (the one for this site is d2u437ei7y3pq8.cloudfront.net). This is AWS’s url for accessing your site. In order to wire up access via your own domain, you need to go back to your domain registrar and register a cname entry for your version of blog.lnicho.com CNAME d2u437ei7y3pq8.cloudfront.net.

That’s it! I’ll update this post in the future for what my monthly cost is for the dozens of views I get every month, but this should be an essentially free hosting of a very performant blog.