jonelantha: Blog

How to setup a Gatsby site on AWS using GitHub Actions - Part 1: S3 and CloudFront

28th February 2020

Let's take a look at how to publish a Gatsby site to AWS using GitHub Actions.

What we'll end up with:

  • A Continuous Deployment (CD) environment hosted with GitHub Actions. Merge your changes into master and within minutes they'll be live for everyone to see.
  • Your site will be hosted on S3 and CloudFront. You'll get great performance, great scalability and you'll enjoy negligible (if any) costs.

NOTE: In this guide we'll setup the S3 bucket to be fully private - no need for some of the hacks and workarounds suggested by other similar guides.

Alternative deployment approaches

This guide does rely on some manual setup so it may not be for everyone. If that sounds like you then you may want to consider a more managed solution, something like AWS Amplify for example - see the Gatsby deployment guide for a full list of alternatives.

However, if you're looking for a more hands-on and bare-metal approach then let's continue!

AWS Account

We'll assume you have an AWS account and you're logged into the console using an administator account you setup according to AWS IAM best practices.

S3 Bucket - where the site files will live

The first step is to create the S3 bucket, this is needed for storing the files which make up the site. Later on we'll setup a GitHub Actions CD workflow to automatically upload the site files to this bucket.

To setup the bucket you'll need to decide a couple of things:

  • Bucket Name: This is a unique name which is representative of your site. It can be anything but a good choice would be your site's domain name (if you have one). An example would be
  • AWS Region: This is the AWS region where the bucket will be hosted, see the AWS Region Guide for more info on choosing regions

Now to create the bucket:

  1. In the AWS console go to the S3 section
  2. Click Create Bucket
  3. On the Name and region screen:

    • Enter the Bucket name and AWS region you've chosen
    • Leave other fields blank and click Next
  4. On the Configure options screen:

    • For this initial setup we don't need anything here (it's easy to change these settings in the future if needed)
    • Click Next
  5. On the Set permissions screen:

    • You should see Block all public access is ticked. Keep it ticked.
    • Click Next again
  6. On the Review screen:

    • Double check the name and region
    • Click Create Bucket

So what did we do here? We created a private S3 bucket which should be locked down and not accessible via the web. The next step is to create the CloudFront distribution so users can access the files in the bucket.

Note it's possible to host your site directly from the S3 bucket without needing CloudFront. There are a number of disadvantages to this approach so we won't be looking at that here but take a look at the AWS S3 Static Hosting documentation for more information.

CloudFront - serve the files from in the Bucket

Now to create the CloudFront distribution. CloudFront is a Content Delivery Network (CDN) which will serve up the files in the bucket - for more information on the advantages of using CloudFront see the Features of CloudFront

  1. In the AWS console go to the CloudFront section
  2. Click Create Distribution
  3. On the Select a delivery method for your content screen:

    • Under the Web section click Get Started
  4. On the Create Distribution screen:

    • In the Origin Settings section:

      • Origin Domain Name: click the field and select the bucket you created earlier
      • Origin Path: fine to leave blank
      • Origin ID: this should now be prefilled, it's fine to leave it as it is
      • Restrict Bucket Access: Select Yes
      • Origin Access Identity: Select Create New Identity
      • Comment: this should now be prefilled with the identity name
      • Grant Read Permissions on Bucket: Select Yes Update Bucket Policy
      • Origin Custom Headers: no need to do anything here
    • In the Default Cache Behavior Settings section:

      • Viewer Protocol Policy: If you know you plan to setup https/ssl then select Redirect HTTP to HTTPS otherwise leave as HTTP and HTTPS
      • Allowed HTTP Methods: leave as GET, HEAD
      • Field-level Encryption Config: leave blank
      • Cache Based on Selected Request Headers: leave as None
      • Object Caching: Leave as Use Origin Cache Headers
      • Minimum TTL, Maximum TTL, Default TTL: should all be unselectable, that's fine
      • Forward Cookies: leave as None (Improves Caching)
      • Query String Forwarding and Caching: leave as None (Improves Caching)
      • Smooth Streaming: leave as No
      • Restrict Viewer Access: leave as No
      • Compress Objects Automatically: set to Yes
      • Lambda Function Associations: no need to do anything here but we will need to setup a Lambda Function in Part 2 of this guide
    • In the Distribution Settings section:

      • Price Class: you can leave this as it is or alternatively select the first option in the list. Click the i icon for more info.
      • AWS WAF Web ACL: leave as None
      • Alternate Domain Names (CNAMEs): leave this blank for now but when setting up your own domain name you'll need to come back to this one
      • SSL Certificate: for now leave as Default CloudFront Certificate
      • Supported HTTP Versions: leave as HTTP/2, HTTP/1.1, HTTP/1.0
      • Default Root Object: enter index.html here
      • Logging: it's easiest to leave as Off for now but you may want to come back to this, click the i icon for more info
      • Enable IPv6: leave ticked
      • Comment: fine to leave this blank
      • Distribution State: Should be left as Enabled
    • Click Create Distribution
  5. On the CloudFront Private Content Getting Started screen:

    • feel free to review this information, when you're done click Distributions on the left
  6. On the CloudFront Distributions screen:

    • you should see a list containing your new distribution
    • click the ID link for the distribution (if you have multiple distributions take a look at the Origin column to figure out the correct row)
  7. On the Distribution screen

    • Here you can view and edit the distribution you've setup
    • On the summary you'll see several fields which we'll need later on:

      • Distribution ID - The unique ID for this distribution within CloudFront
      • ARN - The unique ID for this distribution within the whole of AWS
      • Domain Name - An auto generated domain name to access the site (consider this a temporary domain name as you'll want to setup your own domain name later on)

So what did we setup here? For many of the fields we just selected the defaults but we also granted read-only access to the private S3 bucket for this new distribution. This was accomplished in two steps:

  • We told the console to create a new CloudFront Origin Access Identity, which is basically a way for that particular distribution to identity itself to the S3 bucket. If you're interested in seeing the identity which was created click Origin access identity in the left hand panel of the CloudFront console.
  • The second step was to grant access to the S3 bucket for this new identity. To see how this has was done, go to your S3 bucket in the console, click the Permissions tab, click the Bucket Policy button and you should see the bucket policy which grants access to the new CloudFront identity.

CloudFront - setup the Gatsby error pages

By default CloudFront will show some quite unfriendly messages so we'll need to tell CloudFront to use Gatsby's error pages. CloudFront serves up a 403 error when most users would expect a 404 error so we'll setup both the 403 and 404 errors to show the Gatsby 404 error.

Note at this point the 404.html page won't exist, this step is just preperation for when the Gatsby site is available in the bucket.

  1. Go to the Distribution details screen (see last couple of steps above)
  2. Click the Error Pages tab
  3. In the Error Pages tab

    • Click Create Custom Error Response

      • HTTP Error Code: Choose 403
      • Error Caching Minimum TTL: leave as 600
      • Customize Error Response: set to Yes
      • Response Page Path: enter /404.html
      • HTTP Response Code: set to 404
    • Create a second custom error response for the 404 error by repeating the steps above but this time using 404 as the HTTP Error Code instead of 403.

Surely it would've been easier just to use a CloudFormation template?

Wow that's a lot of setup and this is only Part 1! We could've set this up using a CloudFormation template and it would've been created in seconds. Whilst a CloudFormation is very convenient, it does hide a lot of the details away and it offers very little opportunity for learning - hopefully by doing these steps manually you'll have a better grasp of the components and you'll be in a better position to maintain it in the future.

OK all done. What's next?

Next we need to setup CloudFront to correctly handle Gatsby style urls.

Let's move on to Part 2: CloudFront index Lambda function.

© 2003-2021 jonelantha