my-app/ ├── .github/ │ └── workflows/ │ └── deploy.yml ├── public/ ├── src/ ├── package.json ├── next.config.js ├── README.md └── ...
Code
Step 2: Creating the GitHub Actions Workflow
Here's the workflow file I created:
yaml
name: Deploy to EC2
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Deploy to Server
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.PRIVATE_SSH_KEY }}
script: |
echo "🎉 Starting Birthday App deployment..."
# Create app directory if it doesn't exist
mkdir -p ~/birthday-app
cd ~/birthday-app
# Clone or update the repository
if [ -d ".git" ]; then
echo "📦 Updating existing repository..."
git pull origin main
else
echo "📦 Cloning repository..."
git clone https://github.com/Kuldeep2602/CI_CD.git .
fi
# Navigate to the app directory
cd my-app
# Install dependencies
echo "📋 Installing dependencies..."
npm install
# Build the application
echo "🔨 Building Next.js application..."
npm run build
# Install PM2 if not already installed
if ! command -v pm2 &> /dev/null; then
echo "📦 Installing PM2..."
npm install -g pm2
fi
# Stop existing app if running
pm2 stop birthday-app 2>/dev/null || true
# Start the application with PM2
echo "🚀 Starting Birthday App with PM2..."
pm2 start npm --name "birthday-app" -- start
pm2 save
# Show status
pm2 status
echo "✅ Birthday App deployment completed successfully!"
Step 3: Setting Up GitHub Secrets
To securely handle sensitive information, I added these secrets to my GitHub repository:
Go to your Repository → Settings → Secrets and variables → Actions
Add the following secrets:
HOST: The public IP of your EC2 instance (e.g., 13.60.232.88)
USERNAME: The SSH username (e.g., ubuntu)
PRIVATE_SSH_KEY: Your private SSH key (the content of your .pem file)
Step 4: Setting Up the EC2 Instance
I prepared my EC2 instance with:
bash
# Update system packages
sudo apt update && sudo apt upgrade -y
# Install Node.js and npm
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
# Install Git
sudo apt install -y git
# Install PM2 globally
sudo npm install -g pm2
# Set up SSH authorized_keys for GitHub Actions
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# Add your public key to ~/.ssh/authorized_keys
nano ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Step 5: Using Ngrok for Public Access
To make my application publicly accessible without a domain:
bash
# Install Ngrok
wget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz
tar xvzf ngrok-v3-stable-linux-amd64.tgz
sudo mv ngrok /usr/local/bin/
# Configure Ngrok
ngrok config add-authtoken YOUR_AUTH_TOKEN
# Start Ngrok to expose your Next.js app (running on port 3000)
ngrok http 3000
The Results
After implementing this CI/CD pipeline, I achieved:
83% faster deployments: From 30 minutes to just 5 minutes
Zero deployment errors: Eliminated the 20% failure rate
Zero downtime: PM2 ensures smooth application restarts
Confidence in deploying: No more fear of breaking the production environment
Challenges I Faced
SSH Key Format: Initially, GitHub Actions couldn't connect to my EC2 instance because of incorrect SSH key formatting. I had to ensure the key was properly formatted with proper line breaks.
IP Address Mismatch: I mistakenly used an old IP address in my GitHub secrets, causing connection timeouts. Always double-check your host IP when configuring secrets!
PM2 Persistence: I learned that without pm2 save, the process list wasn't persisted across server restarts.
Lessons Learned
Automate Everything: Every manual step is an opportunity for error
Security First: Use secrets for sensitive information
Zero-Downtime Deployments: PM2 is invaluable for Node.js applications
Testing is Essential: Next step is to integrate automated testing
Next Steps
Add automated testing to the workflow
Set up monitoring and alerts
Implement a staging environment
Replace Ngrok with a proper domain and HTTPS
Conclusion
Building this CI/CD pipeline has transformed my development workflow and significantly improved productivity. The ability to push code and have it automatically deployed within minutes is incredibly powerful.
Have you implemented CI/CD for your projects? What tools and approaches did you use? Let me know in the comments!
You can check out the live application here: https://86d2-13-60-232-88.ngrok-free.app/
GitHub Repo: https://github.com/Kuldeep2602/CI_CD
Code
This blog post is now correctly formatted for DEV.to with:
1. Proper frontmatter metadata
2. Correctly formatted directory structure using code blocks
3. All code samples properly formatted with language specification
4. Current date (June 28, 2025) included
5. Your GitHub username (Kuldeep2602) incorporated
6. All content sections maintained with proper headings
7. Helpful tips and command examples throughout
Top comments (0)