How to Deploy Next.js App to AWS EC2 in Production and Set up CI/CD with Github Actions
Yo!
In this article, we are going through deploying a Next.js app to AWS EC2 and set up continuous integration & deployment by means of Github Actions.
Before proceeding, you will need the infrastructure ready on AWS, which can be deployed manually or by Terraform code. I'm skipping the infra part for the purpose of this article.
First things first, we need to ssh into the ec2 instance. Once you logged in, run this script to install Node.js on the machine:
Now you can install yarn
and pm2
globally:
1source ~/.bashrc
2npm install -g yarn pm2
The next step is to clone your Next.js app repo and cd to the project root directory. To automate the subsequent git pull
commands, we are going to create a ssh key pair and use it.
Create a classic ssh key pair(without a passphrase) first:
1cd ~/.ssh
2ssh-keygen -t rsa -b 4096 -C "[email protected]"
Use github
when you are asked for a file name, then you will have two files github
and github.pub
in the ~/.ssh
directory.
Add the public key to authorized_keys
:
1cat ~/.ssh/github.pub >> authorized_keys
Add the private key to Deploy Keys section in your github repo settings.
1cat ~/.ssh/github
Now you are ready to clone the repo in ssh mode! Phew~
1cd ~
2git clone [email protected]:username/awesome-nextapp.git
Install dependencies and build the next app, and run it in production mode with pm2.
1cd ~/awesome-nextapp
2yarn install
3yarn build
4pm2 start npm --name "nextapp" -- start
Your next.js website should be up and running at this point.
1curl localhost:3000
Create ~/deploy.sh
to use in Github Actions.
1#!/bin/bash
2# use the correct node version for below path
3PATH="$PATH:/home/ubuntu/.nvm/versions/node/v16.13.2/bin/"
4cd ~/awesome-nextapp
5git pull
6yarn install
7yarn build
8pm2 restart nextapp
Try and run the script.
1bash deploy.sh
If you verified the script ran successfully, you are very awesome!
Now, let's move on with settig up Github Actions. The first thing we need to do is to create a yaml
file for it.
1cd ~/awesome-nextapp
2mkdir .github && mkdir -p .github/workflows
3touch .github/workflows/frontend.yml
Copy and paste below content.
1name: Frontend Next.js CI
2
3# on: [push]
4on:
5 push:
6 branches: [ main ]
7 pull_request:
8 branches: [ main ]
9
10jobs:
11 build:
12
13 runs-on: ubuntu-latest
14
15 strategy:
16 matrix:
17 node-version: [16.x]
18
19 steps:
20 - name: Checkout the repo
21 uses: actions/[email protected]
22 - name: Use Node.js ${{ matrix.node-version }}
23 uses: actions/[email protected]
24 with:
25 node-version: ${{ matrix.node-version }}
26 - name: Install yarn
27 run: npm install -g yarn
28 - name: Get yarn cache directory path
29 id: yarn-cache-dir-path
30 run: echo "::set-output name=dir::$(yarn cache dir)"
31 - uses: actions/[email protected]
32 id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
33 with:
34 path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
35 key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
36 restore-keys: |
37 ${{ runner.os }}-yarn-
38 - uses: actions/[email protected]
39 with:
40 path: '**/node_modules'
41 key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
42 - name: Install deps
43 working-directory: .
44 if: steps.yarn-cache.outputs.cache-hit != 'true'
45 run: yarn install
46 - name: Check codebase
47 working-directory: .
48 run: yarn build
49
50 deploy:
51
52 needs: build
53 runs-on: ubuntu-latest
54
55 steps:
56 - name: Install SSH Key
57 uses: shimataro/[email protected]
58 with:
59 key: ${{ secrets.SSH_PRIVATE_KEY }}
60 known_hosts: 'placeholder'
61 - name: Adding Known Hosts
62 run: ssh-keyscan -H ${{ secrets.FE_SERVER_IP }} >> ~/.ssh/known_hosts
63 - name: Run deploy script
64 run: ssh [email protected]${{ secrets.FE_SERVER_IP }} bash deploy.sh
Go to your repo Settings -> Secrets -> Actions secrets and add two secrets:
SSH_PRIVATE_KEY
can be grabbed by cat ~/.ssh/github
on the server
FE_SERVER_IP
can be grabbed by curl ipinfo.io/ip
on the server
Commit the workflow file in the repo and push it, and see how the workflow runs in Actions tab of your repo.
Setting up CI/CD right usually takes some efforts and experiments, if you followed through thus far, that's so cool.
If you are curious about deploying the whole infrastructure(vpc-ec2-alb-acm-route53-s3) as code in Terraform, email me - dockerlead at gmail dot com.
Happy coding! ๐