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)