Skip to content

Jenkins is an open-source, self-hosted automation server written in Java with a very large plugin ecosystem. Concepts are in CI-CD Pipelines.


Architecture

  • Controller (formerly master): web UI, configuration, schedules jobs, stores results. Do not run builds on it
  • Agents (nodes): machines or containers that run the work. Connect by SSH, inbound agent (JNLP/WebSocket), or are created on demand by Docker / Kubernetes plugins
  • Executors: parallel slots on a node
  • Plugins: Git, Pipeline, Docker, Kubernetes, Credentials, Blue Ocean, SonarQube, Slack...
  • JENKINS_HOME: all config, jobs and build history. Back it up

Running Jenkins in Docker

$ docker run -d --name jenkins \
    -p 8080:8080 -p 50000:50000 \
    -v jenkins_home:/var/jenkins_home \
    jenkins/jenkins:lts-jdk17
$ docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

Open http://localhost:8080, unlock with that password, install suggested plugins, create an admin. See 1. Docker and 2. Docker Compose.


Job Types

  • Freestyle: click-configured, legacy
  • Pipeline: code in a Jenkinsfile (preferred)
  • Multibranch Pipeline: scans a repo and creates a pipeline per branch / PR automatically
  • Organization folder: scans a whole GitHub org

Jenkinsfile

Two syntaxes: Declarative (structured, recommended) and Scripted (full Groovy).

pipeline {
    agent any

    environment {
        IMAGE = "myregistry.example.com/shop-api"
        TAG   = "${env.GIT_COMMIT.take(7)}"
    }

    options {
        timestamps()
        timeout(time: 30, unit: 'MINUTES')
        buildDiscarder(logRotator(numToKeepStr: '20'))
    }

    triggers { pollSCM('H/5 * * * *') }

    stages {
        stage('Checkout') {
            steps { checkout scm }
        }
        stage('Test') {
            agent { docker { image 'python:3.12-slim' } }
            steps {
                sh 'pip install -r requirements.txt'
                sh 'pytest --junitxml=report.xml'
            }
            post { always { junit 'report.xml' } }
        }
        stage('Build image') {
            steps { sh 'docker build -t $IMAGE:$TAG .' }
        }
        stage('Push') {
            when { branch 'main' }
            steps {
                withCredentials([usernamePassword(credentialsId: 'registry-creds',
                                                  usernameVariable: 'U', passwordVariable: 'P')]) {
                    sh 'echo $P | docker login myregistry.example.com -u $U --password-stdin'
                    sh 'docker push $IMAGE:$TAG'
                }
            }
        }
        stage('Deploy') {
            when { branch 'main' }
            steps {
                input message: 'Deploy to production?'
                sh 'kubectl set image deployment/shop-api api=$IMAGE:$TAG'
            }
        }
    }

    post {
        success { echo 'Done' }
        failure { echo 'Build failed' }
        cleanup { cleanWs() }
    }
}

Key declarative blocks

Block Purpose
agent Where to run: any, none, label 'x', docker { image '...' }, kubernetes { ... }
environment Env vars, credentials('id') binds a secret
stages / stage / steps The work
when Conditional stages (branch, tag, expression, changeset)
parallel Run stages concurrently
post always, success, failure, unstable, cleanup
parameters Build inputs (string, choice, boolean)
input Pause for human approval
options Timeouts, retries, build retention

Parallel stages

stage('Checks') {
    parallel {
        stage('Lint')  { steps { sh 'ruff check .' } }
        stage('Unit')  { steps { sh 'pytest tests/unit' } }
        stage('Scan')  { steps { sh 'trivy fs .' } }
    }
}

Shared libraries

Reusable Groovy functions stored in a separate repo, loaded with @Library('shared-lib') _ so teams do not copy the same pipeline code.


Credentials and Security

  • Store secrets in Manage Jenkins → Credentials (secret text, username/password, SSH key, file); reference by ID, they are masked in logs
  • Use role-based access control (Role Strategy plugin), enable CSRF protection, use HTTPS behind a reverse proxy
  • Keep Jenkins and plugins updated; plugins are the common attack path
  • Prefer short-lived cloud credentials (AWS role / Azure workload identity) over stored keys
  • Never run untrusted PR code on agents with access to secrets

Triggers

  • Webhook from GitHub / GitLab (best): GitHub hook trigger for GITScm polling
  • Poll SCM (H/5 * * * *): checks the repo on a schedule
  • Cron (H 2 * * *): nightly builds. H spreads load by hashing the job name
  • Upstream job completion, manual, or remote API call

Scaling with Agents

pipeline {
    agent {
        kubernetes {
            yaml '''
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: node
    image: node:20
    command: ["sleep"]
    args: ["infinity"]
'''
        }
    }
    stages {
        stage('Build') {
            steps { container('node') { sh 'npm ci && npm test' } }
        }
    }
}

Jenkins vs Hosted Alternatives

Jenkins GitHub Actions / Azure Pipelines
Hosting You run and patch it Provider runs it
Flexibility Extreme, thousands of plugins Strong, marketplace of actions/tasks
Maintenance High Low
Cost Infrastructure + effort Per-minute / seat
Best for On-prem, air-gapped, bespoke flows Cloud-hosted source and quick setup

See GitHub Actions and Azure Pipelines.