If you follow this exactly, your app will:
- Deploy from GitHub with
.env
replaced each time - Serve over HTTPS automatically
- Keep running after reboots
- Renew SSL certs without downtime
0️⃣ Prerequisites
- VPS with Ubuntu/Debian OR CentOS/AlmaLinux/Amazon Linux
- Domain pointing to VPS IP (A record set in DNS)
- GitHub repo with app code
- SSH access to VPS
- GitHub Actions enabled
1️⃣ Install Required Packages
Ubuntu/Debian
sudo apt update && sudo apt upgrade -y sudo apt install curl git nginx ufw -y curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs sudo npm install -g pm2
CentOS / AlmaLinux / Amazon Linux
sudo yum update -y sudo yum install curl git nginx -y curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash - sudo yum install -y nodejs sudo npm install -g pm2
2️⃣ Create a Repo-Level Deploy Key
ssh-keygen -t rsa -b 4096 -C "deploy-key" # Save as: /home/ubuntu/.ssh/deploy_key_example
Press Enter twice for no passphrase.
3️⃣ Add Public Key to GitHub Repo
cat ~/.ssh/deploy_key_example.pub
- GitHub Repo → Settings → Deploy Keys → Add Deploy Key
- Name:
VPS Deploy Key
- Paste the public key
- ✅ Check “Allow write access” (optional)
- Save
4️⃣ Configure SSH for GitHub
nano ~/.ssh/config
Paste:
Host github-example HostName github.com User git IdentityFile ~/.ssh/deploy_key_example IdentitiesOnly yes
5️⃣ Clone the Repo
sudo mkdir -p /var/www sudo chown $USER:$USER /var/www cd /var/www git clone git@github-example:username/repo-name.git example-app cd example-app
6️⃣ Install Dependencies
npm install
7️⃣ Build the App
Next.js
npm run build pm2 start npm --name "example-app" -- start
React
npm run build
8️⃣ Install SSL
Ubuntu/Debian
sudo apt install certbot python3-certbot-nginx -y sudo certbot certonly --nginx -d example.com
CentOS / AlmaLinux / Amazon Linux
sudo yum install certbot python3-certbot-nginx -y sudo certbot certonly --nginx -d example.com
9️⃣ Nginx Configuration (With SSL)
Ubuntu/Debian
sudo nano /etc/nginx/sites-available/example.com
CentOS / AlmaLinux / Amazon Linux
sudo nano /etc/nginx/conf.d/example.com.conf
Next.js (SSR)
server { listen 80; server_name example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; location / { proxy_pass http://localhost:<PORT_OF_YOUR_APP>; 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; } }
React (Static)
server { listen 80; server_name example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; root /var/www/example-app/build; index index.html index.htm; location / { try_files $uri /index.html; } }
🔟 Enable Config & Restart Nginx
Ubuntu/Debian
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled sudo nginx -t sudo systemctl restart nginx
CentOS / AlmaLinux / Amazon Linux
sudo nginx -t sudo systemctl restart nginx
1️⃣1️⃣ Enable PM2 Auto-Start (Next.js Only)
pm2 save pm2 startup
Follow the command PM2 outputs and run it.
1️⃣2️⃣ Enable Automatic SSL Renewal
sudo certbot renew --dry-run
Add a cron job:
sudo crontab -e
Paste:
0 3 * * * /usr/bin/certbot renew --quiet && /bin/systemctl reload nginx
1️⃣3️⃣ GitHub Actions Deployment Pipeline
Next.js
name: Deploy Next.js to VPS on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Deploy via SSH uses: appleboy/ssh-action@master with: host: ${{ secrets.VPS_HOST }} username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_KEY }} script: | cd /var/www/example-app git pull origin main rm -f .env echo "${{ secrets.ENV_FILE }}" > .env npm install npm run build pm2 restart example-app
React
name: Deploy React to VPS on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Deploy via SSH uses: appleboy/ssh-action@master with: host: ${{ secrets.VPS_HOST }} username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_KEY }} script: | cd /var/www/example-app git pull origin main rm -f .env echo "${{ secrets.ENV_FILE }}" > .env npm install npm run build sudo systemctl restart nginx
1️⃣4️⃣ Add GitHub Secrets
In GitHub Repo → Settings → Secrets → Actions:
-
VPS_HOST
→ VPS IP -
VPS_USER
→ubuntu
(or VPS username) -
VPS_KEY
→ contents of~/.ssh/deploy_key_example
-
ENV_FILE
→ entire.env
file contents
✅ Key Differences
Feature | Next.js (SSR) | React (Static) |
---|---|---|
Build Command | npm run build | npm run build |
Serve Method | PM2 Node.js process | Nginx static serve |
Nginx Role | Reverse proxy to Node server | Directly serve /build |
Port Required | ✅ <PORT_OF_YOUR_APP> | ❌ |
PM2 Required | ✅ | ❌ |
Top comments (0)