Deploy a Next.js Application to AWS EC2 Instance using AWS CodePipeline

Sushil Dhore
10 min readAug 8, 2023

AWS CodePipeline is a continuous integration and continuous delivery (CI/CD) service that automates the build, test, and deployment phases of your software release process. In this blog post, we’ll walk you through the steps to set up a CI/CD pipeline using AWS CodePipeline for deploying a Next.js app on an EC2 instance. By the end of this guide, you’ll have an automated workflow that pushes updates to your app from source code changes to the live environment.

Prerequisites:

1 ) An EC2 instance ( Ubuntu 20 ) with Node.js installed and configured to run the Next.js app. ( Follow below mentioned steps to create next.j sample app )

Before we proceed with setting up AWS CodePipeline, let’s ensure that we have our Next.js application code ready and organized in a directory structure. If you already have your Next.js app on GitHub, you might have already created this directory. If not, follow the steps below to create the necessary directory structure for your Next.js app :

mkdir Pipeline-Demo-Next.js
npm install
# Check Version
node -v
npm -v
npm init -y
# Create Project
npx create-next-app@latest
#Follow steps..
#As you can see, in the above cmd i have given " aws-codepipeline-next.js-demo-app app " name 
cd aws-codepipeline-next.js-demo-app
#Modify next.config.js File


/** @type {import('next').NextConfig} */
const nextConfig = {output: 'export',}

module.exports = nextConfig
npm i serve
npm run build
# Yeahhhhh.... out file is here
#Test Your App with

npx serve out

PM2 is a production process manager for Node.js applications that makes it easy to manage and deploy your Node.js apps in a production environment. With PM2, you can keep your applications running continuously, enable them to auto-restart in case of crashes, and manage them effortlessly as daemon processes. Installing PM2 globally allows you to access its commands from any location on your server, making it a powerful tool for maintaining the stability and availability of your Node.js applications. To install the latest version of PM2, run the following command in your terminal:

sudo npm install pm2@latest -g
#Create one Shell script

sudo vi start-app.sh
            #!/bin/bash
npx serve out

As we prepare to deploy our Next.js app on the EC2 instance, we need to ensure that the startup script, start-app.sh, has the necessary execution permissions. This script will be responsible for starting our Next.js app on the server.

#Granting Execution Permission to the Startup Script :
sudo chmod +x start-app.sh
# Launching the Next.js App with PM2:
pm2 start start-app.sh
# Check Logs with 
pm2 logs 0

Let’s Start with AWS-Codepipeline

1. Create IAM Role for EC2 and AWS CodeDeploy

AWS service roles are used to grant permissions to an AWS service so it can access AWS resources. The policies that you attach to the service role determine which AWS resources the service can access and what it can do with those resources.

Let’s navigate to our AWS Management Console and search for IAM service. Now create a new role for EC2 and AWS CodeDeploy:

Please select : AmazonS3FullAccess

One More IAM role we need to create with
Follow the same steps :
Policy nameTypeAttached as AWSCodeDeployRole

Create a CodePipeline using Github, CodeBuild and CodeDeploy

AWS CodePipeline is a continuous integration and continuous delivery (CI/CD) AWS service that allows you to automate the release process for your application or service. Every time you commit a code change to your source(GitHub, AWS CodeCommit, etc), CodePipeline automatically builds, tests, and deploys your code based on the release process models you define while initializing your CodePipeline. This enables you to rapidly and reliably deliver features and updates.

In the following sections, we will go through the CI/CD pipeline stages:

Step 1: CodePipeline

Step 2: Code Source (CodeCommit or Github)

Step 3: CodeBuild and Build Specification (buildspec.yaml) File

Step 4: CodeDeploy and Application Specification (appspec.yml ) File

Step 5: Adding Shell Script on Server

Step 1: CodePipeline

Let’s navigate to CodePipeline via AWS Management Console and click on Create pipeline:

A new service role for your new pipeline will be created on this page as well. We can let AWS create a new S3 bucket to store artifacts or if you would like to store artifacts on an existing S3 bucket in the same region, you can select “Custom location” and then select your preferred S3 bucket

Step 2: Code Source (CodeCommit or Github)

AWS offers five options to provide the source code for the pipeline: AWS CodeCommit, Amazon ECR, Amazon S3, Bitbucket Cloud (beta) and Github. AWS CodeCommit and GitHub are relatively similar; in this post, we will integrate AWS CodePipeline with GitHub.

After selecting GitHub as the source provider, click on the Connect to GitHub button. You’ll then be prompt to enter your GitHub login credentials
Once you grant AWS CodePipeline access to your GitHub repository, you can select a repository and branch for CodePipeline to upload commits to this repository to your pipeline

Step 3: CodeBuild and Build Specification (buildspec) File

We will now proceed to defining the build provider. AWS offers two options for build provider: AWS CodeBuild and Jenkins.
AWS CodeBuild is a fully managed continuous integration service that compiles source code, runs tests, and produces software packages ready to deploy.
Jenkins is an open source automation tool written in Java with plugins built for continuous integration purpose. If you’re not sure which one to use, check out this article: AWS CodePipeline vs. Jenkins CI Server.

For this post, we will select AWS CodeBuild as our build provider and where we will be prompt to select a project:

We are going to mention Project dependencies and building cmd :

version: 0.2

phases:
install:
runtime-versions:
nodejs: latest
commands:
# Install project dependencies
- node -v
- npm i

build:
commands:
# Build the Next.js app
- npm run build

artifacts:
files:
- '**/*'

# We are keeping our artifacts in S3 bucket
Once we have successfully created a build project, we will be redirected back to this page. Let’s now move on to Step 4: Add Deploy stage by clicking Next
Once we have successfully created a build project, we will be redirected back to this page. Let’s now move on to Step 4: Add Deploy stage by clicking Next

Step 4: CodeDeploy and Application Specification(appspec.yml ) File

AWS CodeDeploy is a fully managed deployment service that automates software deployments to a variety of compute services such as Amazon EC2, AWS Fargate, AWS Lambda, and your on-premises servers. You can update your application with little to no downtime during deployment process. For this post, we will use AWS CodeDeploy as our deploy provider.

In CodeDeploy, an application is simply a name or container used by CodeDeploy to ensure that the correct revision, deployment configuration, and deployment group are referenced during a deployment. So let’s create an application! If you look at the above screenshot, on the left column, under Deploy, right click on Applications and click Open Link in New Tab. Once you’re on Applications page, click on Create application:
Create application for CodeDeploy
Here we will need to create a deployment group for our application that specifies which instances your application revisions are deployed to, along with other deployment options.
For Service role, we will select the CodeDeployRole with AWSCodeDeployRole policy attached to it that we created earlier

We are going to with :
Deployment type : In-place

Select Instance Which we have configure with next.js

For our Environment configuration, we will only select Amazon EC2 Netx.js instances.
a load balancer prevents internet traffic from being routed to instances when they are not ready or are currently being deployed to during CodeDeploy deployments. The exact role the load balancer plays, however, depends on whether it is used in a blue/green deployment or an in-place deployment. For this post, we will not configure a load balancer and will proceed to Create deployment group
Now let’s go back to Add deploy stage browser tab and select the Application name and Deployment group that we just created and click Next to review our pipeline

DO NOT refresh your browser tab if you don’t see your Application name and Deployment group that you just created when you click on textbox for each, it will remove all the data you’ve entered so far for your pipeline. Instead, click on Previous (which will take you back to Add build stage) and then click Next and come back to Add deploy stage. You will now be able to select the Application name and Deployment group that you just created.

Here, we will briefly discuss the application specification file is. AppSpec file is a YAML-formatted or JSON-formatted used by CodeDeploy to manage a deployment and to determine what it should install onto your instances from your application revision in Amazon S3 or GitHub and which lifecycle event hooks to run in response to deployment lifecycle events. For more information, check out AWS CodeDeploy user guide and EC2/On-Premises deployment AppSpec example.

For my application, the appspec.yml is as follows:

version: 0.0
os: linux

files:
- source: /
destination: /home/ubuntu/Pipeline-Demo-Next.js/aws-codepipeline-next.js-demo-app
overwrite: true
file_exists_behavior: OVERWRITE

permissions:
- object: /home/ubuntu/Pipeline-Demo-Next.js/aws-codepipeline-next.js-demo-app/scripts
pattern: "**"
owner: ubuntu
group: ubuntu
mode: 777
type :
- file


hooks:

BeforeInstall:

AfterInstall:

ApplicationStart:
- location: scripts/app_start.sh
timeout: 2500
runas: ubuntu

Step 4: Add Shell Script on GitHub & Server.

And the three simple bash scripts used above are as follows:

1 ) app_start.sh
# Keep this shell script in github ( script/ app_start.sh )

#!/bin/bash

cd /home/ubuntu/Pipeline-Demo-Next.js/aws-codepipeline-next.js-demo-app && /bin/sh deploy.sh

2 ) deploy.sh
# Keep this shell script inside Server ( /home/ubuntu/Pipeline-Demo-Next.js/aws-codepipeline-next.js-demo-app/deploy.sh )

#!/bin/bash

/usr/bin/pm2 restart start-app.sh
#/home/ubuntu/.nvm/versions/node/v20.4.0/bin/pm2 restart start-app.sh

#Keep this shell script on Server

3 ) start-app.sh
#Keep this shell script inside server ( /home/ubuntu/Pipeline-Demo-Next.js/aws-codepipeline-next.js-demo-app/start-app.sh )

#!/bin/bash
npx serve /home/ubuntu/Pipeline-Demo-Next.js/aws-codepipeline-next.js-demo-app/out

#Keep this shell script on Server
My deployment initially failed!!! ❌

There are several reason why you would get this error. For me, my deployment failed because I hadn’t installed the CodeDeploy agent on my EC2 instance. The CodeDeploy agent is a software package that, when installed and configured on an instance, makes it possible for that instance to be used in CodeDeploy deployments. Here is a more detailed article on troubleshooting EC2/On-Premises Deployment Issues

Step 1 : First Attach IAM Role to EC2 Instance : Next.js

Step 2 : Now connect to your EC2 instance by following instructions in this article and install/reinstall the CodeDeploy agent on your EC2 instance:

sudo apt-get update -y
sudo apt install ruby-full -y
sudo apt install wget -y
cd /home/ubuntu
wget https://aws-codedeploy-ap-south-1.s3.ap-south-1.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto
sudo service codedeploy-agent status Or sudo service codedeploy-agent start

If everything goes well during your deployment stage, you will see all six events with “succeeded” status:

Few times my deployment failed and I spent a good amount of time searching for errors and their solutions on Google and reading AWS documentations. Then, here came the best part:

Congratulations! 🎉🎉🎉 You’ve successfully deploy your Next.js application to EC2 via AWS CodePipeline..

Access your Next.js Application on EC2 Public DNS :

You can access your EC2 instance on EC2 Public DNS (IPv4) located under Description in EC2 Management Console, looks something like this:

http://ec2-52-66-243-137.ap-south-1.compute.amazonaws.com:3000/

This site can’t be reached ❌

Copy/paste your Public DNS (IPv4) on another tab and append port :3000 at the end of the DNS address (since our Next.js app is using port 3000 by default). As you will see this will not work and there is nothing to show on browser and the browser connection will timeout.

Add 3000 Port with 0.0.0.0/0 Inbound Security Group Rule

Access the EC2 Public DNS, http://ec2-52-66-243-137.ap-south-1.compute.amazonaws.com:3000/
again and you should be able to see your app content now.

If you like this post, or you are stuck at any point in the above sections and have any questions, feel free to ask them below or message them to me on LinkedIn. I’d be more than happy to help you out.

Thank you for reading! if I have made a mistake somewhere or missed an essential point, please definitely let me know in the comments. 🙏

Sushil Dhore
Sushil Dhore

Written by Sushil Dhore

Technology Evangelist : " 𝐁𝐞𝐥𝐢𝐞𝐯𝐞 𝐈𝐧 𝐈𝐧𝐭𝐞𝐠𝐫𝐚𝐭𝐢𝐨𝐧 𝐎𝐟 𝐓𝐞𝐜𝐡𝐧𝐨𝐥𝐨𝐠𝐢𝐞𝐬"

No responses yet

Write a response