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.Hspreads load by hashing the job name - Upstream job completion, manual, or remote API call
Scaling with Agents
- Label agents (
linux,docker,gpu) and select withagent { label 'docker' } - Ephemeral agents: Kubernetes plugin starts a Pod per build and deletes it afterwards, so builds are clean and elastic (2. Kubernetes Architecture – from Container Images to Running Pods)
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.