Jenkins
Jenkins is the venerable open-source CI server. Pipelines as code via Jenkinsfile, agents, credentials, and the patterns that age well.
CI/CD — Jenkins
EXAMPLE
# ===== The model =====
# - Master / Controller: runs the UI, schedules builds
# - Agents (workers): execute jobs; pinned by label
# - Jobs / Pipelines: scripted or declarative; live in the repo as Jenkinsfile
# ===== Declarative Jenkinsfile (recommended) =====
# Jenkinsfile (root of repo)
pipeline {
agent any
options {
timeout(time: 30, unit: 'MINUTES')
timestamps()
buildDiscarder(logRotator(numToKeepStr: '20'))
}
environment {
NODE_VERSION = '20'
}
stages {
stage('Setup') {
steps {
sh 'corepack enable && npm ci'
}
}
stage('Test') {
parallel {
stage('Lint') { steps { sh 'npm run lint' } }
stage('Typecheck') { steps { sh 'npm run typecheck' } }
stage('Unit') { steps { sh 'npm test -- --coverage' } }
}
}
stage('Build') {
steps { sh 'npm run build' }
}
stage('Deploy') {
when { branch 'main' }
steps {
withCredentials([string(credentialsId: 'deploy-token', variable: 'TOKEN')]) {
sh './scripts/deploy.sh $TOKEN'
}
}
}
}
post {
always { junit 'reports/**/*.xml' }
failure { mail to: 'oncall@example.com', subject: "Build failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}" }
}
}
# ===== Scripted Jenkinsfile (fallback) =====
node('linux') {
stage('Test') {
checkout scm
sh 'npm ci'
sh 'npm test'
}
}
# ===== Agents =====
# Configure agents on Manage Jenkins -> Nodes
# Pin a job to a label:
agent { label 'linux && docker' }
# Docker as the agent (ephemeral):
agent {
docker { image 'node:20-alpine' }
}
# ===== Credentials =====
# Manage Jenkins -> Credentials -> Global -> Add
# Types: secret text, username/password, SSH key, certificate, file
# Use them in pipelines via withCredentials([...]) { ... }.
# ===== Shared libraries =====
# Code shared across many pipelines lives in a Git repo configured under
# Manage Jenkins -> Configure System -> Global Pipeline Libraries.
@Library('my-shared-lib') _
mySharedFunction()
# ===== Triggers =====
triggers {
cron('H 4 * * 1-5') // nightly weekdays
pollSCM('H/15 * * * *') // poll repo every 15 minutes (prefer webhooks)
upstream(upstreamProjects: 'shared-libs', threshold: hudson.model.Result.SUCCESS)
}
# Or: GitHub / GitLab webhooks (Manage Jenkins -> Configure System).
# ===== Plugin essentials =====
# - Pipeline + Pipeline: Stage View
# - Git + GitHub Branch Source (multi-branch jobs)
# - Credentials + Credentials Binding
# - Blue Ocean (modern UI)
# - JUnit (test result reporting)
# - Docker / Kubernetes (ephemeral agents)
# ===== Multi-branch pipelines =====
# Auto-discover branches + PRs; build each on its own Jenkinsfile.
# 'GitHub Organization' job scans an entire org.
# ===== When Jenkins wins =====
# - You self-host CI and need flexibility
# - Complex matrices, agents on diverse OS / hardware
# - On-prem secrets that should not leave your network
# - Heavy plugin ecosystem
# ===== When Jenkins hurts =====
# - Tiny team without Jenkins ops experience
# - Public OSS projects (use GitHub Actions / GitLab CI; cheaper + less admin)
# - Plugin churn / upgrades (manage them deliberately)
# ===== Patterns to internalise =====
# - Pipeline as code (Jenkinsfile in the repo)
# - Multi-branch pipelines + GitHub webhooks
# - Ephemeral agents (Docker / Kubernetes)
# - JUnit + coverage reports as first-class outputs
# ===== Pitfalls =====
# - Click-ops in the UI -> not versioned, drifts from repo
# - Long-running agent VMs with leftover state
# - Plugin sprawl + skipped upgrades -> security debt
# - Secrets in env vars logged accidentally (use withCredentials masking)
Why it matters
Jenkins repays the operational tax with flexibility nothing else matches. Declarative Jenkinsfile in the repo, multi-branch pipelines, ephemeral agents, credentials in the vault — keep these as the floor and Jenkins ages gracefully across team and project change.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Jenkinsfile
pipeline {
agent any
stages {
stage('Test') { steps { sh 'npm ci && npm test' } }
stage('Build') { steps { sh 'npm run build' } }
stage('Deploy') { steps { sh './deploy.sh' } }
}
}
Try it Yourself »
Discussion
Loading…