Skip to main content

Command Palette

Search for a command to run...

CI/CD Pipeline

Published
2 min readView as Markdown

Introduction

In my last blog, I shared how I got started with deploying a Next.js app on an EC2 instance using:

  • EC2 for hosting

  • NVM for Node version management

  • PM2 for keeping the app alive in the background

  • Nginx as a reverse proxy to serve the app directly on port 80

That was a big step, but manually pulling code, building, and restarting the app isn’t scalable. Imagine having to SSH into the server and repeat these steps every time you push new code — it’s time-consuming and error-prone.

This is where CI/CD (Continuous Integration and Continuous Deployment) comes in.

Explanation of CI/CD pipeline

CI/CD stands for Continuous Integration, Continuous Deployment and this is used to push any code changes directly to the production/dev environment.

Setting Up GitHub Actions

I created a GitHub Actions workflow to automate my deployment:

  1. Inside my repo, I added a .github/workflows/deploy.yml file.

  2. Wrote a workflow that triggers on push to the main branch.

  3. It connects to my EC2 instance via SSH, pulls the latest code, installs dependencies, builds the Next.js app, and restarts it with PM2.

Example snippet (simplified):

name: Deploy to EC2

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Deploy via SSH
        uses: appleboy/ssh-action@v0.1.10
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ubuntu
          key: ${{ secrets.EC2_KEY }}
          script: |
            cd /home/ubuntu/my-app
            git pull origin main
            npm install
            npm run build
            pm2 restart next-app

With this in place, all I have to do is push to GitHub → and my EC2 server updates automatically. Magic.

More from this blog

First-Principles Notes

16 posts

First-Principles Notes is a public log of thinking in the open. I break systems down to fundamentals, question assumptions, get things wrong, update my models, and write to learn rather than perform.