Learn how to use GitHub Actions to host and deploy a Next.js React web app to AWS S3. We’ll walk through spinning up a new Next.js app from scratch and configuring it for static export. We’ll then create a bucket in S3 and configure Access Keys, where we’ll use a new GitHub action to automatically deploy changes from our main branch.
🧰 More Resources
GitHub Actions
https://github.com/features/actions
Configure AWS Credentials GitHub Action
https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actions
AWS Docs: How do I configure an S3 bucket for static website hosting?
https://docs.aws.amazon.com/AmazonS3/latest/user-guide/static-website-hosting.html
The beauty of Next.js and static web apps is that they let you run the project pretty much anywhere using object storage, like on AWS S3. But it can be a pain to manually update those files each time.
How can we use GitHub Actions to automate and continuously deploy our app to S3?
GitHub Actions is a free service from GitHub that allows us to automate code tasks.
I previously wrote about how we can use them to automate tasks like running tests on our code and sending notifications to Slack.
They provide a flexible way to automatically run code based on our existing workflows. This provides a lot of possibilities like even deploying our website!
S3 (Simple Storage Service) is an object storage service from AWS. It allows you to store files in the cloud easily making them available around the world.
It also allows you to use these files as a website. Because we can upload an HTML file as an object, we can also configure S3 to access that file as an HTTP request. This means that we can host an entire website right in S3.
Continuous Deployment, often referred to by its acronym CD, is the practice of maintaining code in a releasable state and deploying that code automatically or in short cycles.
Particularly in our use case, we’re going to configure our project so that any time a new update is pushed or merged to the primary Git branch, our website will deploy.
We’re first going to bootstrap a simple Next.js app using the default Next.js starting template and configure it to compile to static files.
If you don’t want to create a Next.js project, you can follow along with even a simple HTML file and not run any of the build commands. But Next.js is a modern way to build dynamic web apps, so we’ll start there.
With our website files ready to go, we’ll create and configure an S3 bucket in AWS where we’ll host our website.
Finally, we’ll create a new GitHub Action workflow that will automatically update the website files in S3 any time a new change occurs on our primary branch (main
).
We’re going to get started with the default template with Next.js.
After navigating to the directory you want to create your project in, run:
yarn create next-app my-static-website
## or
npx create-next-app my-static-website
Note: Feel free to replace my-static-website
with the name of your choice. We’ll use that for the rest of this tutorial.
If you navigate to that directory and run the development command, you should be able to successfully start up your development server.
cd my-static-website
yarn dev
## or
npm run dev
New Next.js App
Next, let’s configure our project to statically compile.
Inside the package.json
file, update the build
script to:
"build": "next build && next export",
What this will do is tell Next to take the website and export it to static files, which we’ll use to host the site.
We can test this out by running the command:
yarn build
## or
npm run build
And once finished, we can look inside of the out
directory and see all of the files of our new website.
Static output from Next.js
Finally, we want to host this on GitHub.
Inside of your GitHub account, create a new repository. This will then provide instructions on how you can add an existing project to that repo.
And once you push our your project to GitHub, we should be ready to set up our new website project!
New repo in GitHub
Follow along with the commits:
To get started with our new S3 Bucket, first log in to your AWS account and navigate to the S3 service.
No buckets in S3
We’ll want to create a new bucket, using the name of our choice, which will be used for the S3 endpoint where our website is hosted. We’ll also want to configure our S3 bucket to be able to host a website.
Note: this tutorial will not walk you through how to host a website on S3, but you can check out my other tutorial that will walk you through hosting a website on S3 step-by-step.
Static website hosting in AWS S3
Once we have our S3 bucket configure as a website, we can go back to our Next.js project folder, run our build command, and then upload all of our files from the out
directory into our new S3 bucket.
S3 Bucket with Static App
And once those files are uploaded and we’ve configured our S3 bucket for website hosting, we should now be able to see our project live on the web!
AWS S3 hosted Next.js app
To get started, we’re going to need to create a new workflow.
If you’re familiar with GitHub Actions, you could create one manually, but we’ll quickly walk through how to do this in the UI.
Navigate to the Actions tab of your GitHub repository and click on “set up a workflow yourself.”
New GitHub Action Workflow
GitHub provides a starting template that we can use for our workflow, though we’ll want to make some changes.
Let’s do the following:
pull_request
definition in the on
propertysteps
except for uses: actions/checkout@v2
So at this point we should be left with:
name: CD
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
This code alone will trigger a process that spins up a new instance of Ubuntu and simply checks out the code from GitHub any time there’s a new change pushed to the main
branch.
Next, once we have our code checked out, we want to build it. This will allow us to take that output and sync it to S3.
This step will differ slightly depending on if you are using yarn or npm for your project.
If you’re using yarn, under the steps
definition, add the following:
- uses: actions/setup-node@v1
with:
node-version: 12
- run: npm install -g yarn
- run: yarn install --frozen-lockfile
- run: yarn build
If you’re using npm, add the following:
- uses: actions/setup-node@v1
with:
node-version: 12
- run: npm ci
- run: npm run build
Between both of these sets of steps, what we’re doing is:
out
directory!And now we can commit that file right to our main
branch which will kick off a new run of our workflow that we can see in our Actions tab.
New workflow in GitHub Actions
To see that it works, we can navigate into that run, select our workflow, and see that all of our steps ran including building our project!
Successful build logs for a GitHub Action workflow
Now that we’re building our project automatically, we want to automatically update our website in S3.
To do that, we’re going to use the GitHub Action aws-actions/configure-aws-credentials and the AWS CLI.
The GitHub Action that we’re using will take in our AWS credentials and configuration and make it available to use throughout the lifecycle of the workflow.
As of now, the Ubuntu instance that GitHub Actions provides allows us to use the AWS CLI as it comes included. So we’ll be able to use the CLI commands right in our workflow.
Alternatively, we could use the S3 Sync action. But by using the AWS CLI, we gain more flexibility to customize our setup, we can use it for additional CLI commands, and it’s also generally nice to get familiar with the AWS CLI.
So to get started, let’s add the following snippet as additional steps in our workflow:
- uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
What the above will do is use the AWS credentials configuration action to set up our AWS Access Key, Secret Key, and region based on our settings.
The AWS Region can be customized to whatever region you typically use with your AWS account. I’m in the northeast United States, So I’ll keep us-east-1
.
The Access Key and Secret Key are credentials that you’ll need to generate with your AWS account. The way our code is set up is that we’ll store those values inside of GitHub Secrets, which will prevent those keys from being leaked. When the action runs, Github changes those values to stars (***) so people can’t access those keys.
So to set up those secrets, we first want to generate Access Keys in AWS.
Navigate to the AWS console. Under the user menu, select My Security Credentials, and then select Create access key.
Creating an Access Key in AWS
This will provide you with two values: the Access key ID and the Secret access key. Save these values, as you won’t be able to access the Secret key ID again.
Finding Secret and Access Key in AWS
Note: remember to NOT include the Access Key and Secret Key inside of your code. This could lead to someone compromising your AWS credentials.
Next, inside of the GitHub repo, navigate to Settings, Secrets, then select New secret.
Here we’ll want to add our AWS keys using the following secrets:
And once saved you should have your two new secrets.
Creating Secrets in GitHub
Now that we have our credentials configured, we should be ready to run the command to sync our project to S3.
Inside of the GitHub Action, add the following step:
- run: aws s3 sync ./out s3://[bucket-name]
Note: be sure to replace _[bucket-name]_
with the name of your S3 Bucket.
This command will trigger a sync with our specified S3 bucket, using the contents of the out
directory, which is where our project builds to.
And now, if we commit our changes, we can see that our action is automatically triggered once committed to the main
branch, where we build our project and sync it to S3!
Successful AWS S3 sync in GitHub Action workflow
Note: Make sure that before setting up this action you’ve configured the S3 bucket to host a website (including unblocking permissions on S3 bucket) – otherwise this action may fail.
At this point, our project probably looks the same, as we didn’t make any changes to the code.
Next.js app on AWS S3
But if you make a code change, such as changing the title of the homepage inside of pages/index.js
and commit that change:
<h1 className={styles.title}>
Colby's <a href="https://nextjs.org">Next.js!</a> Site
</h1>
We can see that our change triggers the workflow to kick off:
New GitHub Action workflow from code change
And once our workflow finishes, we can see that our content is now automatically updated on our website:
AWS S3 hosted app with updated code changes
Follow along with the commits:
The goal of this post wasn’t to go through the entire process of configuring a website for AWS, but if you’re serving a website on S3, you might want to also include CloudFront in front of it.
You can check out my other guide here which walks you through setting up CloudFront as well as a step-by-step guide through creating the site in S3.
If your S3 website is behind CloudFront, chances are, you’ll want to make sure CloudFront isn’t caching the new changes.
With the AWS CLI, we can also trigger a cache invalidation with CloudFront to make sure it’s grabbing the latest changes.
Check out the docs here to learn more.
#github #next #aws #javascript #react