Docs / Programming & Development / CI with Jenkins on VPS

CI with Jenkins on VPS

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 238 views · 3 min read

Jenkins remains the most flexible CI/CD platform, offering complete control over your build infrastructure. Running Jenkins on your own VPS provides unlimited build minutes, data privacy, and integration with any tool. This guide covers installing Jenkins, creating pipelines, and optimizing for production use.

Installation

# Install Java (required)
sudo apt install openjdk-21-jre-headless

# Add Jenkins repository
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/" | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null

sudo apt update && sudo apt install jenkins
sudo systemctl enable --now jenkins

# Get initial admin password
sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Nginx Reverse Proxy

server {
    listen 443 ssl http2;
    server_name jenkins.example.com;

    ssl_certificate /etc/letsencrypt/live/jenkins.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/jenkins.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_request_buffering off;
    }
}

Declarative Pipeline

// Jenkinsfile
pipeline {
    agent any

    environment {
        DOCKER_IMAGE = "myapp:${BUILD_NUMBER}"
        DEPLOY_HOST = "production.example.com"
    }

    stages {
        stage("Checkout") {
            steps {
                checkout scm
            }
        }

        stage("Build") {
            steps {
                sh "docker build -t ${DOCKER_IMAGE} ."
            }
        }

        stage("Test") {
            steps {
                sh "docker run --rm ${DOCKER_IMAGE} npm test"
            }
            post {
                always {
                    junit "test-results/*.xml"
                }
            }
        }

        stage("Security Scan") {
            steps {
                sh "trivy image --exit-code 1 --severity HIGH,CRITICAL ${DOCKER_IMAGE}"
            }
        }

        stage("Deploy") {
            when { branch "main" }
            steps {
                sshagent(credentials: ["deploy-key"]) {
                    sh """
                        docker save ${DOCKER_IMAGE} | ssh ${DEPLOY_HOST} docker load
                        ssh ${DEPLOY_HOST} "docker stop myapp || true && docker rm myapp || true"
                        ssh ${DEPLOY_HOST} "docker run -d --name myapp -p 8080:8080 ${DOCKER_IMAGE}"
                    """
                }
            }
        }
    }

    post {
        failure {
            slackSend(channel: "#builds", message: "Build FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
        }
        success {
            slackSend(channel: "#builds", message: "Build passed: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
        }
    }
}

Docker-in-Docker Agents

// Pipeline with Docker agent
pipeline {
    agent {
        docker {
            image "node:20-alpine"
            args "-v /tmp:/tmp"
        }
    }
    stages {
        stage("Install") { steps { sh "npm ci" } }
        stage("Test") { steps { sh "npm test" } }
        stage("Build") { steps { sh "npm run build" } }
    }
}

Essential Plugins

  • Blue Ocean: Modern UI for pipeline visualization
  • Docker Pipeline: Build and use Docker containers in pipelines
  • Git: Source control integration
  • Pipeline: Jenkinsfile support
  • Credentials Binding: Securely inject secrets
  • SSH Agent: SSH key management for deployments
  • Slack Notification: Build status notifications

Security Best Practices

# Restrict access
# Manage Jenkins > Security > Configure Global Security
# - Enable "Logged-in users can do anything" or use Matrix Authorization
# - Disable "Allow anonymous read access"

# Use credentials store for secrets
# Manage Jenkins > Credentials > System > Global credentials
# Never put secrets in Jenkinsfile

# Restrict agents
# Use Docker agents to isolate builds
# Limit what agents can access

Resource Optimization

# /etc/default/jenkins
JAVA_ARGS="-Xmx1g -Xms512m -Djava.awt.headless=true"
JENKINS_ARGS="--httpPort=8080 --httpListenAddress=127.0.0.1"

# Clean old builds automatically
# In pipeline: options { buildDiscarder(logRotator(numToKeepStr: "10")) }

# Disk cleanup
# Manage Jenkins > System > Workspace Cleanup

Summary

Jenkins on a VPS provides unlimited CI/CD capacity under your control. Pipeline-as-code with Jenkinsfile keeps build configuration versioned alongside your application code. Docker agents provide clean, reproducible build environments without polluting the Jenkins host. For teams needing full control over their CI/CD infrastructure without per-minute billing, a self-hosted Jenkins instance on a VPS is the most cost-effective solution.

Was this article helpful?