Deploy React (CRA) App to Amazon S3 using Github Actions

Here, I will walk through one of the ways of deploying Create React App to Amazon S3 using GitHub actions.

Create an S3 bucket in AWS

  1. Go to the S3 service in AWS.

  2. Go to Create Bucket page.

  3. Enter the bucket name. (e.g. my-site). Uncheck Block all public access and select the acknowledgment checkbox.

  4. Submit.

Update permissions and properties of the bucket

Now that you have the created S3 bucket in your bucket list, open that bucket and follow the below steps:

  1. Go to Permissions tab of the bucket.

  2. Scroll to Bucket Policy, edit and add following policy there:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-site/*"
        }
    ]
}

Note: Replace "my-site" with your actual bucket name.

  1. After saving the above changes, go to Properties of the bucket and scroll down to "Static website hosting".
  • Enable Static website hosting.

  • Add "index.html" in the input of Error document similar to the Index document. It is required because of the SPA (Single Page Application) nature of create-react-app. Otherwise, when we open any URL directly from the address bar, it would lead to a 404 S3 error.

  1. After saving the above changes, you will find the Bucket website endpoint under "Static website hosting" of S3 properties. That will be our web app's URL.

Create an IAM user for GitHub Actions

  1. Go to AWS IAM. Navigate to Policies from the sidebar and click Create policy.

  2. Select the JSON tab and paste the following code after replacing "my-site" with your S3 bucket name. Make sure bucket name is correctly added at both places.

{
    "Version": "2012-10-17",
    "Statement": [
            {
              "Sid": "AllowS3SyncCommand",
              "Effect": "Allow",
              "Action": [
                  "s3:GetObject",
                  "s3:Listbucket",
                  "s3:PutObject",
                  "s3:DeleteObject"
              ],
              "Resource": [
                    "arn:aws:s3:::my-site",
                    "arn:aws:s3:::my-site/*"
              ]
            }
    ]
}
  1. Skip the Tags step and name your policy (e.g. "my-site-github-action").

  2. Once that's created, navigate to User groups from left sidebar and click Create group.

  3. Name it (e.g. "my-site") and select the above-added policy in the Attach permissions policies section.

  4. After creating it, navigate to Users from left sidebar and click Add users.

  5. Enter name (e.g. "my-site-github-actions"). Select Access key - Programmatic access in "Select AWS access type".

  6. Select the user group that is created in the above step. Skip the Tags step, review and create a user.

  7. Make a note of AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_REGION.

Setup GitHub Actions

  1. Open your GitHub repository. Go to Settings > Secrets > Actions. Add the above secrets to your GitHub repository.

  2. Create .github/workflows/deploy.yml and add the following code.

  # .github/workflows/deploy.yml

  name: Deploy

  on:
    push:
      branches: ["main"]
    workflow_dispatch:

  jobs:
    build:
      runs-on: ubuntu-latest

      steps:
        - uses: actions/checkout@v3

        - name: Install packages
          run: npm install

        - name: Build React app
          run: npm run build
          env:
            REACT_APP_API_URL: ${{ secrets.REACT_APP_API_URL }} 
            # add other env similarly

        - name: Configure AWS Credentials
          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: ${{ secrets.AWS_REGION }}

        - name: Deploy to S3 bucket
          run: aws s3 sync ./build/ s3://my-site --delete

Note: Replace bucket name or branch name based on your code. You can also add a bucket name as a secret.

That's it. Once you push the next commit to the main branch, it will auto-deploy it. You can visit the site with the URL we mentioned above.


Setup Multi-environment deployment (optional)

If you want multiple environments for deployment like staging and production, then you can set the secrets based on different branches and add them in the above workflow file.

However, at the time of writing this, GitHub only provides branch-based environments for Pro or Team accounts. So, in that case, you can do a workaround with a prefix depending on the branch to load environment variables like below:

  # .github/workflows/deploy.yml

  name: Deploy website

  on:
    push:
      branches: ["staging", "main"]
    workflow_dispatch:

  jobs:
    build:
      runs-on: ubuntu-latest

      steps:
        - uses: actions/checkout@v3

        - name: Set develop env
          if: ${{ github.ref == 'refs/heads/staging' }}
          run: echo "prefix_env=DEV" >> $GITHUB_ENV

        - name: Set main env
          if: ${{ github.ref == 'refs/heads/main' }}
          run: echo "prefix_env=PROD" >> $GITHUB_ENV

        - name: Install packages
          run: npm install

        - name: Build React app
          run: npm run build
          env:
            REACT_APP_API_URL: ${{ secrets[format('{0}_REACT_APP_API_URL', env.prefix_env)] }}

        - name: Configure AWS Credentials
          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: ${{ secrets.AWS_REGION }}

        - name: Deploy to S3 bucket (Staging)
          if: ${{ github.ref == 'refs/heads/staging' }}
          run: aws s3 sync ./build/ s3://my-site-staging --delete

        - name: Deploy to S3 bucket (Production)
          if: ${{ github.ref == 'refs/heads/main' }}
          run: aws s3 sync ./build/ s3://my-site-prod --delete

Note: You need to create different S3 buckets with the same process mentioned above for staging and production. While adding environment variables in GitHub secrets, you have to prefix like DEV_REACT_APP_API_URL and PROD_REACT_APP_API_URL for all of your environment variables.

Summary

Here, we have seen the process of deploying our static single-page React application to AWS S3. Also, you can create an environment-based deployment with the process we covered in the last section.

Hope this has helped you deploy your application to S3.