The automation server that has run CI/CD for two decades and still powers countless pipelines. You'll run Jenkins locally, write a pipeline as code, wire in stages, credentials, and parallelism, and adopt the patterns that keep Jenkins maintainable instead of a snowflake. Work top to bottom, or jump to a part.
The journey:
| You need | Why |
|---|---|
| Docker installed | The fastest way to run Jenkins locally |
| A Git repository | Pipelines build code from source control |
| Basic Git + shell familiarity | Pipelines are steps run on an agent |
No cloud account is required. If Git is new to you, do the Git tutorial first. Confirm Docker works: docker run --rm hello-world.
Jenkins is an automation server: it runs jobs (build, test, deploy) triggered by events (a push, a schedule, a button). The core pieces:
| Term | What it is |
|---|---|
| Controller | The Jenkins brain — schedules work, stores config, serves the UI |
| Agent (node) | A worker that actually runs the build steps |
| Job / Pipeline | The unit of automation you define |
| Executor | A slot on an agent that runs one build at a time |
Modern Jenkins is pipeline-as-code: you commit a Jenkinsfile to your repo, and Jenkins runs it. Avoid click-configured "freestyle" jobs — they don't version, review, or reproduce.
docker run -d --name jenkins -p 8080:8080 -p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts-jdk17
# Grab the initial admin password:
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
Open http://localhost:8080, paste the password, install the suggested plugins, and create your admin user. The jenkins_home volume persists all config, jobs, and history — treat that directory as the crown jewels.
The
lts(Long-Term Support) line is what you want in production — pinned and stable, not the bleeding-edge weekly release.
A declarative pipeline lives in a Jenkinsfile at your repo root:
pipeline {
agent any // run on any available agent
stages {
stage('Build') {
steps {
sh 'echo Building... && make build'
}
}
stage('Test') {
steps {
sh 'make test'
}
}
}
}
In Jenkins: New Item → Pipeline → Pipeline script from SCM, point it at your repo. Jenkins reads the Jenkinsfile, runs each stage, and shows a Stage View — green/red per stage — so failures are obvious at a glance.
A realistic pipeline validates, builds, tests, and reacts to the outcome:
pipeline {
agent any
options { timeout(time: 20, unit: 'MINUTES') } // don't hang forever
environment { REGISTRY = 'ghcr.io/acme' }
stages {
stage('Build') {
steps { sh 'docker build -t $REGISTRY/app:$GIT_COMMIT .' }
}
stage('Test') {
steps { sh 'make test' }
}
}
post {
success { echo 'All green' }
failure { echo 'Build failed — notify the team' }
always { junit 'reports/*.xml' } // publish test results either way
}
}
stages run in order; a failed stage stops the pipeline.post blocks (success, failure, always, unstable) react to the result — notifications, cleanup, publishing artifacts.options and environment set guardrails and shared variables.Polling is wasteful; let Git tell Jenkins.
pipeline {
agent any
triggers {
// Webhook-driven builds are best; this is a fallback poll:
pollSCM('H/5 * * * *')
cron('H 2 * * *') // also build nightly at ~02:00
}
stages { /* ... */ }
}
The right setup is a webhook: configure your GitHub/GitLab repo to POST to Jenkins on push and PR, so builds start instantly. Use a Multibranch Pipeline so Jenkins auto-discovers branches and PRs and runs the Jenkinsfile for each. The H ("hash") in cron spreads load instead of firing everything on the same minute.
Never hard-code secrets in a Jenkinsfile. Store them in Manage Jenkins → Credentials, then bind them at run time:
stage('Deploy') {
steps {
withCredentials([string(credentialsId: 'prod-api-token', variable: 'API_TOKEN')]) {
sh './deploy.sh' // $API_TOKEN available only inside this block
}
}
}
Jenkins masks credential values in the console log. Use scoped credential types (username/password, SSH key, secret file, secret text) and grant each job only what it needs. For cloud deploys, prefer short-lived tokens or an IAM role over long-lived keys.
Speed up by running independent work at once:
stage('Test') {
parallel {
stage('unit') { steps { sh 'make test-unit' } }
stage('integration') { steps { sh 'make test-integration' } }
stage('lint') { steps { sh 'make lint' } }
}
}
For "the same job across many combinations," use a matrix:
matrix {
axes {
axis { name 'OS'; values 'linux', 'windows' }
axis { name 'NODE'; values '18', '20', '22' }
}
stages {
stage('test') { steps { sh 'make test' } }
}
}
Run heavy or environment-specific builds on labelled agents (agent { label 'docker' }). Ephemeral agents — spun up per build in Kubernetes or Docker — keep builds clean and reproducible, versus long-lived pets that accumulate drift.
When ten repos copy-paste the same deploy logic, extract a shared library — versioned Groovy in its own repo — and call it:
@Library('acme-pipelines') _
standardPipeline(
language: 'node',
deployTo: 'production'
)
The library defines reusable steps (vars/standardPipeline.groovy) so every team gets the same tested build/test/deploy flow, and you fix bugs in one place. This is how large orgs keep hundreds of pipelines consistent.
Jenkinsfile in the repo; avoid freestyle jobs. Consider Configuration-as-Code (JCasC) for the controller itself.JENKINS_HOME — it's all your config and history.failure.Jenkinsfile.withCredentials; never echo them.options { timeout(...) }.jenkins_home, unlock it, and install the suggested plugins.Jenkinsfile with Build and Test stages to a repo, create a Pipeline-from-SCM job, and read the Stage View after a run.post { success/failure/always } blocks and a junit step, then make a test fail on purpose and watch the pipeline react.parallel unit/integration/lint stages and compare the wall-clock time.withCredentials, and confirm it's masked in the console log.Self-check:
Jenkinsfile preferable to a freestyle job?You now have the full loop: run Jenkins → pipeline-as-code → stages and triggers → secrets → parallelism → shared libraries → harden. That's Jenkins as teams actually run it.
Learned the concepts? Test yourself with Jenkins interview questions.
Go to the question bank →