Automate Gatsby Static Site Deployment to AWS S3 Using CodePipeline and CodeBuild

Nick Ramkissoon
Geek Culture
Published in
6 min readSep 7, 2021
Photo by James Harrison on Unsplash

Introduction + Prerequisites

Gatsby is a great frontend framework for quickly developing and deploying static websites. There is a rich ecosystem of beautiful templates and plugins for a variety of use cases such as blogs and portfolio sites.

There’s several services out there where you can easily deploy static websites for cheap, however in this article we’ll be going over how to automate deploying to AWS using CodePipeline, CodeBuild, and S3. We will:

  1. Connect a CodePipeline pipeline to a GitHub repository with our code.
  2. Create an S3 bucket and configure it to host a website to the public.
  3. Create a CodeBuild project that will build our code and deploy it to our S3 bucket.

The contents of this article is aimed at AWS beginners/intermediates.

Prerequisites

  1. A GitHub repository with a website you want to deploy. If you need an example, you can try using this blog theme to start out. The instructions in the README will tell you how to get started using it.
  2. An AWS account.

S3 Setup

Using an S3 bucket for website hosting is a common use case and the configuration for it is relatively simple.

  1. Go to the S3 and create a new bucket.
  2. Enter a bucket name. If you will be using a custom domain, you should name the bucket that (example.com).
  3. Uncheck ‘Block all public access’. We need people on the internet to be able to access our website assets inside the bucket.
  4. Click Create Bucket

Now that the bucket has been created, navigate to it:

  1. Got to the Properties tab and edit ‘Static website hosting’
  2. Enable Static website hosting, select ‘Host a static website’ for Hosting Type.
  3. For Index document and Error document, enter ‘index.html’ and ‘404.html’ respectively, or whatever HTML files your site builds as the index and error document.
  4. Click Save changes
  5. Finally, go to the Permissions tab and enter this JSON into the Bucket Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<BUCKET_NAME>/*"
}
]
}

Your S3 bucket should be ready to go.

CodePipeline Setup

Now we will create a Codepipeline that will execute every time we push to our GitHub repository. Navigate to the CodePipeline console and hit Create Pipeline. In Step 1, pick a name and select New service role for the pipeline and go on to the next step.

GitHub Source

In Step 2 we will be connecting to our GitHub account and adding our repository as a source stage in CodePipeline. Under Source provider, select GitHub (Version 2). This will open up more options below:

  1. For Connection, select an existing connection or, if you have never connected your AWS account to GitHub before, click on Connect to GitHub. This will prompt you to log in to GitHub and verify that you want to create a connection.
  2. After your create a connection, select the Repository name and Branch name you want to use.
  3. Leave all the other options default as they are not needed for this pipeline. Go to the next step.

CodeBuild

In Step 3, select CodeBuild as the Build provider and then click on Create Project to open up a new window to create a new CodeBuild project. Enter a name and leave the default options for now. We will go over the details of the CodeBuild step in the next section.

Step 4 is the ‘Add a deploy stage’ step in CodePipeline. Because we will be copying our static site build files to S3 in the build step, we can skip this stage and move on to create the pipeline.

CodeBuild Setup

Navigate to Build Projects section of the CodeBuild console, you should see the name of the project you created while setting up the pipeline.

Select the build project and go to Edit then Buildspec.

It’s common for the buildspec.yml file to be included in the repository so it can also be version controlled, but for this project we’ll insert our build commands directly into the AWS console.

Be sure to replace BUCKET_NAME with the actual name of your S3 bucket.

buildspec.yml

version: 0.2

phases:
install:
commands:
- "touch .npmignore"
- "npm install -g gatsby"
pre_build:
commands:
- "npm install"
build:
commands:
- "npm run build"

post_build:
commands:
- 'aws s3 sync "public/" "s3://<BUCKET_NAME>" --delete --acl "public-read"'

artifacts:
base-directory: public
files:
- "**/*"
discard-paths: yes

This is a simple buildspec. It installs our dependencies to build a Gatsby site, then runs npm run build which would be defined in package.json. In the post_build phase, we use the AWS CLI to copy the static site assets (which we expect to be in the public/ directory) to the S3 bucket we are using to host the site.

Attaching Policies to Allow Writing to S3 Bucket

In order to give our build project permission to access our S3 bucket, we need to attach an appropriate S3 access policy to the build project’s service role. To do this:

  1. Find the project’s service role by navigating to the Build details tab. The service role is listed under Environment. Click on the service role link to open up IAM.
  2. Attach the AmazonS3FullAccess policy to the service role. Note, this is not recommended in production because the policy gives access to all buckets and all actions. You should create a policy that only defines write access to the specific S3 bucket.

Once this is done, try executing the pipeline and see if it runs to completion. If all goes well, you should be able to see your build files in the S3 bucket, and you should see your website by going to the bucket’s URL.

Bonus: CloudFront Distribution + AWS Certificate Manager + Custom Domains

We should do a couple of things to make our website more production ready.

The S3 URL isn’t exactly pretty and we should be using a custom domain. We also want to be able to access our website using SSL/TLS, and therefore need a certificate. Lastly, if we want to lower latencies visitors experience, we should host our assets in a CDN. The following sections will outline how to achieve all of the above.

AWS Certificate Manager for SSL/TLS + Custom Domain Setup

If you have a custom domain, Amazon can provide a SSL/TLS certificate for it using AWS Certificate Manager. Request a certificate for your domain in the Certificate Manager console and follow the steps to verify that you own your domain.

After verification is complete, if you are not going to create a CloudFront distribution, you can add a CNAME record pointing to the S3 bucket URL so that you can access you website using HTTPS.

CloudFront Distribution Setup

Setting up a CloudFront CDN distribution is simple when using S3 to host.

  1. Go to the CloudFront console and click on Create distribution
  2. Select your S3 bucket as the Origin domain and enter a name for the origin.
  3. In Default cache behavior, select Redirect HTTP to HTTPS if you have set up an SSL certificate.
  4. In Settings, select a custom SSL certificate if you have one created and enter index.html into the Default root object.
  5. Click Create distribution

If you go to the distribution domain name in your browser, you should be able to see your website (you may have to wait a few minutes).

Finally, if you are using a custom domain name, you should create a CNAME record pointing to the distribution domain name so that you can access you website hosted on the CDN via your custom domain.

Conclusion

In this article, we were able to create a automated pipeline for deploying a static Gatsby website to S3. We are also able to add a custom domain and host our website assets in a CloudFront distribution. In general, hosting any static website on AWS will follow a similar infrastructure pattern.

In the future you may wish to add testing stages to your pipeline before deployment, further tweak the CDN, etc. Hopefully, this post has taught you enough to get you started with CI/CD and static site hosting on AWS!

--

--

Nick Ramkissoon
Geek Culture

Software engineer that loves to educate and help people.