Deploy Static Web Apps to AWS S3 Using GitHub Actions

Gaurish NaikGaurish Naik
3 min read

This guide will help you securely deploy a static web application (e.g., Next.js, Vite) to an AWS S3 bucket using GitHub Actions and an IAM Role.

Step 1: Create an IAM Role in AWS

  1. Log in to the AWS Management Console and navigate to IAM > Roles.

  2. Click Create Role.

  3. Select Web Identity as the trusted entity type.

  4. Under Identity Provider, choose token.actions.githubusercontent.com. If it doesn’t exist:

  5. Return to the Create Role screen, select token.actions.githubusercontent.com, and confirm sts.amazonaws.com as the audience.

  6. Add Permissions: Choose a policy like AdministratorAccess (for simplicity) or a custom policy with least privilege for production.

  7. Set Trust Policy: Ensure the policy matches the following (replace placeholders with your values):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:<GITHUB_USERNAME>/<REPO_NAME>:ref:refs/heads/<BRANCH_NAME>"
        }
      }
    }
  ]
}
  1. Name the Role (e.g., GitHubActionsRole) and click Create Role.

Step 2: Configure GitHub Repository Secrets

  1. Go to your GitHub repository.

  2. Navigate to Settings > Secrets and Variables > Actions.

  3. Add the following secrets:

    • AWS_ROLE_ARN: The ARN of the IAM role (e.g., arn:aws:iam::<AWS_ACCOUNT_ID>:role/GitHubActionsRole).

    • AWS_REGION: Your AWS region (e.g., us-east-1).

    • S3_BUCKET: The name of your S3 bucket (e.g., my-app-bucket).

Step 3: Create a GitHub Actions Workflow

  1. In your repository, create a file at .github/workflows/deploy.yml.

  2. Add the following YAML configuration to automate the deployment to S3:

name: Deploy to S3

on:
  push:
    branches:
      - main

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Verify AWS Role Assumption
        run: aws sts get-caller-identity

      - name: Install Dependencies
        run: npm install

      - name: Build Application
        run: npm run build

      - name: Deploy Next.js to S3
        run: |
          if [ -d "out" ]; then
            echo "✅ Found 'out/' directory (Next.js export). Deploying..."
            aws s3 sync out/ s3://${{ secrets.S3_BUCKET }}/ --delete --cache-control "public, max-age=0, must-revalidate"
          else
            echo "❌ 'out/' directory not found. Build may have failed."
            exit 1
          fi

      - name: Deploy Vite to S3
        run: |
          if [ -d "dist" ]; then
            echo "✅ Found 'dist/' directory (Vite). Deploying..."
            aws s3 sync dist/ s3://${{ secrets.S3_BUCKET }}/ --delete --cache-control "public, max-age=0, must-revalidate"
          else
            echo "❌ 'dist/' directory not found. Build may have failed."
            exit 1
          fi

Explanation of the Workflow

  • Trigger: The workflow is triggered when a push is made to the main branch.

  • Permissions: Grants access to the GitHub OIDC token and repository contents.

  • Steps:

    1. Checkout code: Uses actions/checkout to pull the latest code.

    2. Configure AWS Credentials: Uses aws-actions/configure-aws-credentials to authenticate using the IAM role.

    3. Verify Role Assumption: Confirms that the AWS role has been assumed successfully.

    4. Install Dependencies: Runs npm install to install the necessary packages.

    5. Build Application: Executes npm run build to generate the production build.

    6. Deploy to S3: Syncs the build output (either out/ for Next.js or dist/ for Vite) to the S3 bucket, ensuring the removal of outdated files and setting proper cache headers.

Notes

  • Replace placeholders: Ensure that you replace <AWS_ACCOUNT_ID>, <GITHUB_USERNAME>, <REPO_NAME>, and <BRANCH_NAME> with your actual values.

  • Least-privilege policy: For production environments, use a least-privilege IAM policy instead of AdministratorAccess.

  • S3 static website hosting: If you’re serving the app directly from S3, make sure your S3 bucket is configured for static website hosting

0
Subscribe to my newsletter

Read articles from Gaurish Naik directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gaurish Naik
Gaurish Naik