Skip to content

Instantly share code, notes, and snippets.

@SpiffGreen
Last active October 11, 2023 10:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SpiffGreen/2eaaf0160cb7dee8dfb291c8c2aba42e to your computer and use it in GitHub Desktop.
Save SpiffGreen/2eaaf0160cb7dee8dfb291c8c2aba42e to your computer and use it in GitHub Desktop.
EC2 standard setup with nginx and ssl

Deploying a Nextjs Application on EC2

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 :)

Requirements

For setting up the application we need the following

  • An AWS account
  • And of course the source code available on github

Configuration Steps

For a complete setup follow the steps below

Setup an EC2 Instance

Install system dependencies

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

Setup github action to deploy app

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;

  1. It first gets the current source code to the ubuntu device
  2. 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.
  3. 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 the npm i and npm ci command is that the latter doesn't install development dependencies.
  4. During the fourth step, the application is built, while the environment variables needed are placed in a .env file
  5. 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.

Setup Nginx

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";
        }
    }
}

Setup SSL

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

Custom Domain

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.

  1. Create a hosted zone with your domain name
  2. In the hosted zone, create an A record pointing to the public IP address of the EC2 server
  3. Optionally, you can add a www. subdomain with a CNAME record pointing to your domain name.

Conclusion

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.

References

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";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment