CI/CD with VPS: Complete A to Z Guide

From theory to practical implementation - Build a real-world CI/CD pipeline for your VPS

Git Integration
Auto Deploy
VPS Ready
Production Ready

Part 1: Theory & Fundamentals

What is CI/CD?

Continuous Integration (CI)

  • Developers merge code changes into a central repository frequently
  • Automated builds and tests run on each commit
  • Catches bugs early and improves software quality

Continuous Deployment (CD)

  • Automatically deploys code changes to production after passing tests
  • Reduces manual deployment errors
  • Enables rapid feature delivery

CI/CD Pipeline Flow

Code Push → Git Repository → CI/CD Service → Build → Test → Deploy to VPS → Health Check

Part 2: Prerequisites

Before diving into implementation, make sure you have the following ready:

1
A VPS (DigitalOcean, Linode, AWS EC2, etc.)
2
A Git Repository (GitHub, GitLab, or Bitbucket)
3
A CI/CD Service (GitHub Actions or GitLab CI)
4
SSH Access to your VPS
5
Basic knowledge of Linux commands
6
Node.js application (or any other stack)

VPS Initial Setup

bash
# Update system
sudo apt update && sudo apt upgrade -y

# Install Node.js (for Ubuntu)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Install nginx (reverse proxy)
sudo apt install -y nginx

# Install PM2 (process manager)
sudo npm install -g pm2

Part 3: Practical Implementation

Step 1: Create Your Application

Let's create a simple Node.js Express application with health checks:

javascript
// app.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({
    message: 'Hello from CI/CD Pipeline!',
    version: '1.0.0',
    timestamp: new Date().toISOString()
  });
});

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'healthy' });
});

const server = app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

// Graceful shutdown
process.on('SIGTERM', () => {
  console.log('SIGTERM received, closing server...');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});

module.exports = app;

Step 2: Set Up SSH Keys for Deployment

Generate SSH keys for secure, automated deployments:

bash
# Generate SSH key pair (run locally)
ssh-keygen -t ed25519 -C "cicd@yourproject" -f ~/.ssh/cicd_deploy_key

# Copy public key to VPS
ssh-copy-id -i ~/.ssh/cicd_deploy_key.pub YOUR_USER@YOUR_VPS_IP

# Test SSH connection
ssh -i ~/.ssh/cicd_deploy_key YOUR_USER@YOUR_VPS_IP

# Get private key for CI/CD secrets
cat ~/.ssh/cicd_deploy_key

Step 3: GitHub Actions CI/CD Pipeline

Create .github/workflows/deploy.yml in your repository:

yaml
name: CI/CD Pipeline

on:
  push:
    branches: [ main, master ]

env:
  NODE_VERSION: '20.x'
  APP_DIR: /home/deploy/app
  APP_NAME: myapp

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Deploy to VPS
      uses: appleboy/ssh-action@v1.0.0
      with:
        host: ${{ secrets.VPS_HOST }}
        username: ${{ secrets.VPS_USERNAME }}
        key: ${{ secrets.VPS_SSH_KEY }}
        script: |
          cd ${{ env.APP_DIR }}
          git pull origin main
          npm ci --production
          pm2 restart ${{ env.APP_NAME }}
          pm2 save
    
    - name: Health Check
      run: |
        sleep 10
        curl -f http://${{ secrets.VPS_HOST }}/health

Step 4: VPS Preparation Script

Run this script on your VPS to prepare it for deployment:

bash
#!/bin/bash
set -e

echo "🚀 Starting VPS setup for CI/CD..."

# Update system
apt update && apt upgrade -y

# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs

# Install nginx
apt install -y nginx

# Install PM2
npm install -g pm2

# Create deployment user
useradd -m -s /bin/bash deploy

# Create app directory
mkdir -p /home/deploy/app
chown -R deploy:deploy /home/deploy/app

# Setup PM2 startup
su - deploy -c "pm2 startup systemd -u deploy --hp /home/deploy"

# Configure nginx
cat > /etc/nginx/sites-available/app << 'EOF'
server {
    listen 80;
    server_name _;
    
    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;
    }
}
EOF

ln -sf /etc/nginx/sites-available/app /etc/nginx/sites-enabled/app
rm -f /etc/nginx/sites-enabled/default

nginx -t
systemctl restart nginx

echo "✅ VPS setup complete!"

Step 5: Configure GitHub Secrets

Add these secrets to your GitHub repository (Settings → Secrets and variables → Actions):

VPS_HOST

Your VPS IP or domain (e.g., 192.168.1.100)

VPS_USERNAME

SSH username (e.g., deploy)

VPS_SSH_KEY

Private SSH key content (from ~/.ssh/cicd_deploy_key)

VPS_PORT

SSH port (default: 22) - Optional

Testing Your Pipeline

Deploy Your First Version

  1. 1.Commit and push your code to the main branch
  2. 2.Check the Actions tab in GitHub to see your pipeline running
  3. 3.Wait for tests to pass and deployment to complete
  4. 4.Visit your VPS IP or domain to see your app live!
bash
# Test your deployment
curl http://YOUR_VPS_IP/

# Check health endpoint
curl http://YOUR_VPS_IP/health

# View PM2 status on VPS
ssh deploy@YOUR_VPS_IP
pm2 status
pm2 logs myapp

Best Practices & Tips

Security

  • Rotate SSH keys regularly
  • Use environment variables for secrets
  • Enable firewall (UFW) on VPS
  • Keep dependencies updated

Performance

  • Use PM2 cluster mode for scaling
  • Enable Nginx caching
  • Monitor with PM2 Keymetrics
  • Set up proper logging

Reliability

  • Always run tests before deploy
  • Implement health checks
  • Set up error alerting
  • Keep backups of your code

Development

  • Use staging environment first
  • Test deployments locally
  • Document your pipeline
  • Version your infrastructure

🎉 You're All Set!

You now have a fully automated CI/CD pipeline that deploys your application to a VPS whenever you push to the main branch.

Start building, commit your changes, and watch your code automatically deploy to production!