First I tried App runner service, then amplify, then elastic beanstalk but non met the requirements of my application, so I had to resort to doing it the had way :)
For setting up the application we need the following
- An AWS account
- And of course the source code available on github
For a complete setup follow the steps below
The linux environment will need some tools to run the server which include nginx as our proxy server, nodejs and pm2. Run the commands below to install them
Nginx
sudo apt update
sudo apt install nginx
Nodejs
The new installation script - [From Nodesource]
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt-get update
sudo apt-get install nodejs -y
PM2 Using pm2 as your process manager, it allows you keep your applications alive forever with a built-in load balancer. Find its documentation here. To install run the command blow
npm install pm2 -g
You can confirm the installation
nginx -v
node -v
npm -v
In the root of your nextjs project create a file with this path .github/workflows/main.yml
with the content below.
name: Deploy CI
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "18"
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: |
# You can run a few commands
touch .env
echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" >> ".env"
echo "NEXTAUTH_URL=https://example.com" >> ".env"
echo "NEXT_PUBLIC_BASE_URL=https://example.com" >> ".env"
npx prisma generate
npm run build
ls -la
- name: Deploy to Server
uses: easingthemes/ssh-deploy@main
with:
SSH_PRIVATE_KEY: ${{ secrets.EC2_SSH_KEY }}
REMOTE_HOST: ${{ secrets.HOST_DNS }}
REMOTE_USER: ${{ secrets.USERNAME }}
TARGET: ${{ secrets.TARGET_DIR }}
SCRIPT_AFTER: |
if pm2 describe 'npm run start' > /dev/null; then
pm2 restart 'npm run start' --update-env
else
pm2 start npm --name 'npm run start' -- start
fi
The github action template shown above is ran whenever there's a push or merge to the main branch, the job run on an ubuntu machine (github runner). And it does the following;
- It first gets the current source code to the ubuntu device
- It sets up nodejs with version 18, so the rest of the just has access to node and npm, as you see they can be used in subsequent steps.
- In the third step, the packages needed for the application's source code to be built and run are installed using the
npm ci
command which is for clean install. The difference between thenpm i
andnpm ci
command is that the latter doesn't install development dependencies. - During the fourth step, the application is built, while the environment variables needed are placed in a .env file
- The file step, here the script deploys the built files to the server via ssh and also issues some commands to either start or restart the server depending on the applications state.
Run the script below to setup docker
#### INSTALL DOCKER (ubuntu 24x) ####
sudo apt-get update -y
sudo apt install docker.io -y
sudo usermod -aG docker $USER
Run or use the script below as user data
sudo apt-get update -y
sudo apt-get install ruby -y
sudo apt-get install wget -y
cd /home/ubuntu
wget https://aws-codedeploy-eu-west-1.s3.eu-west-1.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto
Use the nginx configuration below but replace the domain name *example.com with yours:
worker_processes auto;
events {
worker_connections 1024;
}
http {
# Set the MIME type mappings
include mime.types;
default_type application/octet-stream;
# Set the logging configuration
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Set the proxy settings
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Configure the server
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}
To enable https you have to setup an SSL certificate. You can get a free certificate using certbot. Run the commands below and follow the prompt.
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo systemctl status certbot.timer
sudo certbot renew --dry-run
For the domain name configuration. It's a lot easier if you hand over control of the dns to aws's route 53 service. This means if you got your domain from a registrar other than aws, you should use a custom dns setting on the registrar, so records can be created on route 53. At this point. You can do the following to point your domain to the ec2 instance.
- Create a hosted zone with your domain name
- In the hosted zone, create an A record pointing to the public IP address of the EC2 server
- Optionally, you can add a www. subdomain with a CNAME record pointing to your domain name.
In this comprehensive guide, we've embarked on a journey to master the deployment of Next.js applications on AWS EC2, harnessing the power of GitHub Actions for automation, securing our applications with SSL/TLS via Let's Encrypt, configuring custom domains, and managing Node.js applications efficiently with PM2. We hope this step-by-step walkthrough has demystified the deployment process and empowered you to take control of your Next.js projects. By following the strategies outlined here, you can seamlessly launch your applications, ensuring both high performance and robust security. Whether you're a seasoned developer or just starting your journey, this guide provides the knowledge and tools to navigate the intricacies of deployment on AWS EC2, leaving you with a rock-solid foundation for your web projects. Remember, the road to successful deployment might have its twists and turns, but with this guide in hand, you're well-prepared to tackle the challenges and embark on your journey toward a seamless, scalable, and secure Next.js application.