Software Development

Deploy Next.js to AWS using SST

Save Money and Win Control over Your Next.js Apps

Deploy Next.js to AWS using SST
This image has been generated by AI.

If you want to save money with hosting, and learn valuable skills like CI/CD, then this article is for you.

You will learn useful AWS skills, CI/CD pipelines using Github Actions, all of which are in-demand skills that pay very well in the industry.

AWS is not as challenging as it seems when using SST, which configures the necessary AWS services to host your Next.js apps with ease.

So without further ado… Let’s dive right in!

1. Configuring AWS

Most tutorials on SST skip this step; they assume you know how to do this already.

This article is different.

I will not cut any corners, and I will explain the entire process so you can avoid searching thousands of articles to look for answers.


Configuring AWS consists of three main steps:

  1. Create an AWS Account
  2. Create an IAM User
  3. Set Up the AWS CLI

Create an AWS Account

Creating an AWS account is very straightforward. Here is the link to register.

If you need further assistance, Ashok IT explains the account creation process in just 2 minutes:

Create an IAM User

Now we need to create an IAM (Identity and Access Management) user to allow us to programmatically interact with AWS.

This process was daunting at first, but this article by SST explains this process extremely well using pictures at every step.

After you are finished, make sure to copy your Access key and Secret Access Key somewhere safe. This is necessary for the next step.

Set Up the AWS CLI

The installation process differs for Windows and Unix operating systems, so I will make sure to cover both:

Windows Installation

In Windows, you can run the following command in Powershell or CMD:

msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2.msi

After completing the installation process, you can verify that the CLI works by restarting your terminal and running aws --version.

Unix Installation

1. Make sure both python and pip are installed on your system.

2. Install the awscli package by running this command:

sudo pip install awscli

Now run aws configure, and enter the Access Key ID and Secret Access Key from the previous section on Creating an IAM User.

2. Configuring SST for Next.js

1. Before we begin, make sure you have a Next.js app by running the following command:

pnpm create next-app

2. cd into your project directory, and run the following command to initialize SST:

pnpm create sst
pnpm install

3. Create a file called .env.local, then add the AWS key and secret:

AWS_ACCESS_KEY_ID=<YOUR_KEY_ID>
AWS_SECRET_ACCESS_KEY=<YOUR_KEY>

4. Find the sst.config.ts file in the root of your project directory, then configure it with any settings you want.

Here is my config file as a reference:

import { Certificate } from "aws-cdk-lib/aws-certificatemanager"
import { SSTConfig } from "sst"
import { NextjsSite } from "sst/constructs"

export default {
  config(_input) {
    return {
      name: "portfolio",
      region: "us-east-1",
    }
  },
  stacks(app) {
    app.stack(function Site({ stack }) {
      const site = new NextjsSite(stack, "site", {
        // ... Your environment variables here
        environment: {
          AUTH_SECRET: process.env.AUTH_SECRET!,
          AUTH_GOOGLE_ID: process.env.AUTH_GOOGLE_ID!,
          AUTH_GOOGLE_SECRET: process.env.AUTH_GOOGLE_SECRET!,

          TURSO_AUTH_TOKEN: process.env.TURSO_AUTH_TOKEN!,
          TURSO_DATABASE_URL: process.env.TURSO_DATABASE_URL!,

          AWS_ARN_CERTIFICATE: process.env.AWS_ARN_CERTIFICATE!,
        },
        customDomain: {
          domainName: "www.danielfullstack.com",
          isExternalDomain: true,
          cdk: {
            certificate: Certificate.fromCertificateArn(
              stack,
              "MyCert",
              process.env.AWS_ARN_CERTIFICATE!
            ),
          },
        },
        // ... Add more configuration options
      })

      stack.addOutputs({
        SiteUrl: site.url,
      })
    })
  },
} satisfies SSTConfig

3. Creating a CI/CD Pipeline With Github Actions

Now that we have setup AWS, bootstrapped SST with Next.js, we are ready to deploy our application using a CI/CD (Continuous Integration/Continuous Integration) pipeline.

A CI/CD pipeline is a set of predefined commands that are executed sequentially on a remote computer (when you push changes to Github).

These commands typically consist of setting up node.js, installing dependencies, and deployment.


To get started, create a new folder called .github/workflows in the root of your directory, then add a file called deploy.yml.

I encourage you to read all of the comments (#) in this file for a better understanding of how all this works:

name: Deploy

on:
  # This command lets you automatically start the pipeline
  # inside Github Actions.
  workflow_dispatch:
  # This pipeline will only execute when you push to the main
  # or master branch for your Github repository.
  push:
    branches:
      - master
      - main

jobs:
  deploy:
    # The OS for your remote machine.
    runs-on: ubuntu-latest

    steps:
        # Perform git checkout to fetch code from the 
        # latest branch.
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install pnpm
        uses: pnpm/action-setup@v4.0.0
        with:
          version: "8.15.1"

        # The next two steps are needed to cache pnpm dependencies
      - name: Get pnpm store directory
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

      - uses: actions/cache@v4
        name: Setup pnpm cache
        with:
          path: ${{ env.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install dependencies
        run: pnpm install

        # Similarly to Vercel, we cache the build output so
        # it takes less time to redeploy our app.
      - name: Cache build output
        uses: actions/cache@v4
        with:
          path: |
            ~/.pnpm
            ${{ github.workspace }}/.next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-

        # We deploy our SST Next.js app using the 
        # environment variables defined inside "Github Secrets"
      - name: Deploy
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          
          AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
          AUTH_GOOGLE_ID: ${{ secrets.AUTH_GOOGLE_ID }}
          AUTH_GOOGLE_SECRET: ${{ secrets.AUTH_GOOGLE_SECRET }}
          
          TURSO_AUTH_TOKEN: ${{ secrets.TURSO_AUTH_TOKEN }}
          TURSO_DATABASE_URL: ${{ secrets.TURSO_DATABASE_URL }}
          
          AWS_ARN_CERTIFICATE: ${{ secrets.AWS_ARN_CERTIFICATE }}
        run: |
          npx sst deploy --stage prod

If you need more help with this, be sure to leave me a comment and I will help you promptly.


You may have noticed in the last comment I talked about "Github Secrets", so lets configure that now.

1. Start by following this URL:

https://github.com/<GITHUB-NAME>/<YOUR-REPO>/settings/secrets/actions

Make sure to replace <YOUR-REPO> and <GITHUB-NAME>

2. Copy your environment variables from your .env.local file into Github Secrets.


You should now be able to commit and push your changes, then watch your application deploy on this page:

https://github.com/<GITHUB-NAME>/<YOUR-REPO>/actions

NOTE: This process can take up to 10 minutes for the first deployment. If you encounter any bugs during this process, please let me know so I can help you.

4. Next Steps

Congratulations! You should now have a Next.js app running on AWS!

Your application should be hosted on a Cloudfront distribution.

Your next step should be to point your custom domain to your Cloudfront Distribution, and I recommend following this guide to understand how to do this properly.

Also feel free to ask for help.

I have implemented this myself, and I am happy to help. 😉


If you enjoyed this article, please make sure to Subscribe, Clap, Comment and Connect with me today! 🌐

References

2