Deploy NextJS to AWS EB with Github Actions in 2021
Tags: aws, github, nextjs, elasticbeanstalk
Published: 2021-10-06 05:34:35

Before I start, I want to acknowledge my teammates at [FYC Labs](https://fyclabs.com/), Zahnen and Adolfo, as well as a post written here in [June 2020 from Michael Weibel](https://dev.to/mweibel/deploying-next-js-on-aws-elasticbeanstalk-ab4). This saved me some headaches (although I still had a few in working on this).

While working on deploying a recent NextJS converted web app, I ran into a few issues, and after additional eyes from my teammates and the article, I was able to put together a current (and I write current since this will evolve eventually) Github Action that can build a NextJS project and get it deployed to AWS Elastic Beanstalk (EB) with minimal fuss. 

Let's review the steps in this example specific to a deploy to a QA/staging environment. 

- We want to Gzip the latest NextJS build after all approved pull requests are merged into QA. 

- We want to increment the version for our latest deployment. 

- We want the app to work. 

For brevity's sake I'll note why:

### AWS Beanstalk 

EB is a tool that makes it easy to deploy and scale applications. Here is the trick, there's not a lot of customization. You choose the platform, zip up your code and press a button. 

While this is effective in allowing AWS to handle:
- App health monitoring (complete with downloadable logs)
- Capacity Provisioning
- Load Balancing
- Security Updates

This isn't good if you have customization or need to live debug the deployment.

### Github Actions 

What we want to do, since we are depending on another resource to manage scale and capacity, we should go ahead and employ CI/CD to take care of the deployments for us. 

When running through this, you should of course have already setup your EB Instance and be able to click into the interface to monitor the health of the app (you will be able to in Github Actions too, but using the EB interface does offer some live health updates that Github Actions may not express. 

At the core of this post is the Github Action, this example assumes you have a QA branch. 

```yaml
name: QA Deploy

on:
  push:
    branches:
      - "qa"

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - uses: actions/setup-node@v2
        with:
          node-version: "14"
    
      - name: Generate build number
        id: buildnumber
        uses: einaregilsson/build-number@v3
        with:
          token: ${{secrets.github_token}} 

      - name: Generate deployment package
        run: npm run next-install && npm run generate-zip

      - name: Deploy to EB
        uses: einaregilsson/beanstalk-deploy@v18
        with:
          aws_access_key: ${{ secrets.AWS_ACCESS_KEY }}
          aws_secret_key: ${{ secrets.AWS_SECRET }}
          application_name: name-of-your-project
          environment_name: name-of-your-project-staging
          version_label: name-of-your-project-${{ steps.buildnumber.outputs.build_number }}
          region: us-east-1
          deployment_package: deploy.zip
```

This yaml file includes two nice Github action packages from Einar Egilsson: one is a simple build number provider and the other is his beanstalk-deploy, which is great for this exercise. If you'd rather use semver, I plan to update this article in the future with a way to do this. But for this, let's name our deployments with simple English. The version will read like this in the EB application detail view: name-of-your-project-1 (then 2, 3, etc.)

### Scripts to Build and GZIP the Build

You'll notice two scripts in the generate deployment package, one installs and builds the project, the other zips the final build. I recommend that you create a `scripts` folder with these `sh` scripts in them:

```
scripts
  - generate-zip.sh
  - next-install.sh
```

In our `next-install` script:

```sh
rm -rf .next && npm install && npm run build
```

We want to be sure that we are generating a fresh next build, removing the build directory before the new build should overwrite errors that may have appeared in previous builds. 

Placing these commands in script files cleans up the deploy script and ensures sound organization and steps to the process. 

Here is the `generate-zip` script:

```sh
zip deploy.zip -r .next package.json next.config.js public .npmrc
```

The reason we are including the public folder is so that the build has access to the assets that you have placed inside of it. 

The `.npmrc` file is in here in case you have any private or paid packages that you need to provide the auth for. If you don't the EB won't install those packages and your deployment will break.

If you have additional configs from other plugins that you are using, include those in the script so they can be compressed as well.  

### Next Steps and Notes

For troubleshooting, try building locally and running `next start`. Then try a manual deployment by clicking upload in the EB application detail interface.

Make sure that your `package.json` provides a node version:

```json
  "engines": {
    "node": "14"
  },
```

Also, in your `package.json` make sure that you have the scripts you need in the action setup, and set the port to `8080` for `next start`

```json
    "build": "next build",
    "start": "next start -p 8080",
    "lint": "next lint",
    "generate-zip": "sh ./scripts/generate-zip.sh",
    "next-install": "sh ./scripts/next-install.sh"
```

### This was brief, sorry!

This works for a sound CI/CD process. In this example we used `qa`, but if you need this for a production deployment on a `main` branch, simply copy the yaml file and paste it into a `productionDeploy.yml` or however you name your production deployments. You would only need to change the environment name and the branch you are pushing to. 

Again, this was meant as a brief intro to this, but one that hopefully prevents and/or won't lead to headaches when working in this unique stack.

Shoutouts (I don't know you Einar, but thanks for the GH Action Packages):
- [Einar Egilsson](https://github.com/einaregilsson)
- [Michael Weibel](https://github.com/mweibel)
- My teammates Zahnen and Adolfo!