A practical flow using GitHub Actions, S3/CloudFront (or ECS), and safe rollouts for production-ready deployments.

Deploying a Next.js app to AWS gives you full control, predictable costs, and real production experience. In this guide, we’ll deploy a Next.js app on an EC2 instance, run it with PM2, serve it through Nginx, and automate deployments using GitHub Actions.
GitHub → GitHub Actions → EC2
├── PM2 (runs Next.js)
└── Nginx (reverse proxy)Launch an EC2 instance with Ubuntu 22.04 LTS (t2.micro).
Security group: allow SSH (22), HTTP (80), HTTPS (443).
chmod 400 your-key.pem ssh -i your-key.pem ubuntu@YOUR_EC2_PUBLIC_IP
sudo apt update && sudo apt upgrade -y # Node.js (LTS) curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt install -y nodejs node -v npm -v # PM2 sudo npm install -g pm2 # Nginx sudo apt install -y nginx sudo systemctl start nginx sudo systemctl enable nginx
Visit http://YOUR_EC2_IP — you should see the Nginx default page.
cd /var/www sudo git clone https://github.com/your-username/your-repo.git sudo chown -R ubuntu:ubuntu your-repo cd your-repo npm install npm run build pm2 start npm --name "nextjs-app" -- start pm2 save pm2 startup
Your app should now be running on port 3000.
sudo nano /etc/nginx/sites-available/nextjs
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}sudo ln -s /etc/nginx/sites-available/nextjs /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx
Your app is now accessible via port 80 🎉
sudo apt install certbot python3-certbot-nginx -y sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com sudo certbot renew --dry-run
Create an SSH key, add it to the EC2 authorized_keys, and store the private key in GitHub Secrets (EC2_HOST, EC2_USER, EC2_KEY).
name: Deploy Next.js App
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Deploy to EC2
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_KEY }}
script: |
cd /var/www/your-repo
git pull origin main
npm install
npm run build
pm2 restart nextjs-appEvery push to main triggers a deploy: SSH → pull → build → PM2 restart. 🚀
App crashes after reboot
pm2 startup pm2 save
502 Bad Gateway
Check PM2 and port 3000, then:
pm2 logs
Permission issues
sudo chown -R ubuntu:ubuntu /var/www
This setup is scalable, cheap, production‑ready, and portfolio‑worthy. If you understand and can explain this workflow, you’re ahead of most frontend developers.