How to deploy a nextjs app to AWS EC2 with Github action
Tags: aws, react, nextjs, javascript
Published: 2021-08-22 00:40:45

---
canonical_url: "https://www.thangphan.xyz/posts/how-to-deploy-a-nextjs-app-to-aws-ec2/"
---
I want to deploy an application to AWS. In the first time, I was overwhelmed with the architecture of AWS and couldn't find tutorials for people without knowledge AWS. So I wrote this post for people who don't want to spend too much time on cloud.

### Basic concepts

#### AWS ECR

AWS ECR is where consists of all encrypted docker images. AWS ECS will consume these docker images.

#### AWS ECS

AWS ECS controls the way your images is run. It has 3 main concepts: cluster, service, task definitions. 

* Cluster: the center of ECS that consist of services, tasks.
* Service: help you control tasks how task run, how many tasks will run, etc.
* Task definitions: where you can add docker image.

#### AWS EC2

You can think it helps us secure and computes the size of our applications. We can restrict IPs that have permission to access our application.

I'm going to use this flow to deploy my next app.

![Flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ntwkb4qa21hfp5kah9dt.png)

### Setup project

#### Next app

- Let's run this command to initiate a next app with `create-next-app`:
```tsx
npx create-next-app --typescript
```
- Give it a name: `demo-deploy` hit enter and wait for it runs.

- We need to create a docker file for our app. Let's do it:
```docker
FROM node:16-alpine3.11

WORKDIR /demo-deploy

ENV PORT 80

COPY . /demo-deploy

RUN npm install

CMD ["npm", "run", "dev"]
```

#### Setup AWS

##### Create IAM user

We have to have an account in order to interact with AWS console. You need to create a [IAM](https://portal.aws.amazon.com/billing/signup#/start) user.

##### Setup AWS ECS

- You have to setup an IAM user **Administrator**, a **key pair** for EC2 in order to unlock ECS. Click [here](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/get-set-up-for-amazon-ecs.html) and do as instructed.

![Administrator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o3957qg9feccki01xwq2.png)

And hit `Administrator` to create a couple of access key - access secret we need to use later.
![Access Key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7nlyo04dy71eyhglbw3.png)

- We're going to create cluster, service and task definition for AWS ECS. Click [here] (https://console.aws.amazon.com/ecs/home?region=us-east-1) in order to move on the console screen.

Select `Create Cluster` then choose `EC2 Linux Networking`,
click `Next step`. There are three important things you need to set up as the image below. 

![ECS Cluster](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/91lwu53oiki43juqid55.png)

> Note: `key pair` which you have just created in previous steps.

Click `Create new Task Definition` select `EC2`

![Demo Task](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kw3kkba41en7cvpauq8a.png)

Hit `Add container` and config like the image below. The first time image we set `null` temporary.
It will be updated  by the image from ECR that was uploaded through `Github action`.

![Demo container](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iy19ndu9fb5kt4jl4p8x.png)

Finally, we will create `service`:
![Demo service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfbipcm6iz5gv9ltqsdq.png)

##### Setup Github action

- In the nextjs app we're going to create config file `.github/workflows/pipeline.yml`.

```yml

name: Deploy to Amazon ECS

on:
  release:
    types: [created]

env:
  AWS_REGION: ap-northeast-1 # set this to your preferred AWS region, e.g. us-west-1
  ECR_REPOSITORY: demo-deploy # set this to your Amazon ECR repository name
  ECS_SERVICE: demo-service # set this to your Amazon ECS service name
  ECS_CLUSTER: demo-cluster # set this to your Amazon ECS cluster name
  ECS_TASK_DEFINITION:
    .aws/task-definition.json # set this to the path to your Amazon ECS task definition
    # file, e.g. .aws/task-definition.json
  CONTAINER_NAME:
    demo-container # set this to the name of the container in the
    # containerDefinitions section of your task definition

defaults:
  run:
    shell: bash

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@13d241b293754004c80624b5567555c4a39ffbe3
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@aaf69d68aa3fb14c1d5a6be9ac61fe15b48453a2

      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          # Build a docker container and
          # push it to ECR so that it can
          # be deployed to ECS.
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
      - name: Fill in the new image ID in the Amazon ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@97587c9d45a4930bf0e3da8dd2feb2a463cf4a3a
        with:
          task-definition: ${{ env.ECS_TASK_DEFINITION }}
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ steps.build-image.outputs.image }}

      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@de0132cf8cdedb79975c6d42b77eb7ea193cf28e
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true
```
You only have to change some values in `env:` that you just created in `ECS` steps and a value for `AWS_REGION`. 

- Create a file json `.aws/task-definition.json`. You need to go to the `ECS console` hit `Task Definitions` and copy content from `JSON` tab then paste it to `task-definition.json`.

- Create new repo on `Github` with name `demo-deploy`.
Then hit `Settings` -> `Secrets`, create new secret for this repo with name `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` that you created in `Setup AWS ECS` step.

![Secrets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eg17jcu05yns16z6wlfa.png)

### Release repo, confirm result

- We need to commit code to the remote repo. And trigger an action `release` then `Github action` will deploy to `AWS ECS`.

![Github action](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5bh61gcea0n43tjyk64g.png)

- We need to come back `AWS ECS`, access the `service` we just created before.
![Success service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5u8v63f7gbg6b1mrlyo1.png)

- Hit `the content` in task tab we will see
![Success task](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5yk1lahn1r69x1ktot4r.png)

- Click id in `EC2 instance id` we will redirect to `EC2 console`. 

![EC2 instance](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwjamnwuwcjs5cxixjqh.png)

- Click `Open address` in `Public IPv4 DNS` we are able to view our app. 

![Next app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9l20x96iz6dsdi0dvvsr.png)

If it doesn't work, you will have to change `https` -> `http` or see the VPC ID in `EC2 instance` and go to `Security group` in order to add `Inbound rules`.

![Security group](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1hyf2ek0prf1376tyk1r.png)

I hope this guide was helpful for those of you looking to deploy a next app to AWS ECS. Feel free to refer [source code ](https://github.com/thangphan37/demo-deloy).