A complete reference pipeline for learning and mastering Jenkins CI/CD with the following workflow:
Setup Guide: See SETUP_GUIDE.md for step-by-step installation and configuration.
Checkout → Static Analysis → Unit Tests → Build Artifact → Store Artifact →
Docker Build → Push to Registry → Deploy → Smoke/Health Tests → Rollback on Failure
This pipeline uses Spring Petclinic — a well-maintained Spring Boot microservice with:
- Maven build
- Unit tests (excluding integration tests for speed)
- Checkstyle for static analysis
- Spring Boot Actuator (
/actuator/healthfor smoke tests) - Java 21
| Component | RHEL/Alma Linux |
|---|---|
| Jenkins | 2.400+ with Pipeline plugins |
| Java | 21 (Maven wrapper included) |
| Docker | For container build & deploy |
| Git | For checkout |
Install via Manage Jenkins → Plugins:
- Pipeline
- Multibranch Pipeline (branch discovery, branch-to-env)
- Pipeline: Stage View (Stage View grid: columns=stages, rows=builds, durations, status)
- Docker Pipeline
- Git
- JUnit
- Copy Artifact
- SSH Agent (required for remote deployment)
- Blue Ocean (optional, modern pipeline UI)
- SonarQube Scanner (optional)
- JaCoCo (optional, for coverage)
- New Item → Multibranch Pipeline (not "Pipeline")
- Branch Sources → Add source → Git
- Repository URL: This repo (or your fork) with Jenkinsfile + scripts
- Credentials: For private repos
- Build configuration → Mode: by Jenkinsfile → Script Path:
Jenkinsfile - Save → Scan Multibranch Pipeline Now
The pipeline discovers branches (main, stage, uat, test, develop, dev) and builds each. Environment is derived from branch.
| Branch | Deploys to |
|---|---|
main / master |
prod |
stage |
stage |
uat |
uat |
test / develop / dev |
test |
| Other branches | test (default) |
Push to branch triggers build and deploy to that environment. Prod requires manual approval.
- Ensure main, stage, uat, test (or develop) exist in your repo
- Set SKIP_SONAR =
trueif SonarQube is not configured - Set DEPLOY_METHOD =
jarfor remote deployment without registry - Configure webhook for auto-build on push (see SETUP_GUIDE)
Environment is derived from branch (no manual parameter):
| Branch | Env | Host (default) | Health URL |
|---|---|---|---|
| main | prod | 192.168.31.124 | http://192.168.31.124:8080/actuator/health |
| stage | stage | 192.168.31.123 | http://192.168.31.123:8080/actuator/health |
| uat | uat | 192.168.31.122 | http://192.168.31.122:8080/actuator/health |
| test/develop/dev | test | 192.168.31.121 | http://192.168.31.121:8080/actuator/health |
Edit the branchToEnv and envConfig maps in the Jenkinsfile to match your setup. Prod deployments require manual approval.
The pipeline is Stage View compatible. The Pipeline: Stage View plugin displays a grid on the job page:
- Columns = pipeline stages (Environment Setup, Checkout, Static Code Analysis, etc.)
- Rows = build runs (#63, #62, #61...)
- Cells = status (success/failed/aborted) + duration
- Average stage times = shown below the grid
No code changes needed—the plugin reads stage() blocks from the Declarative Pipeline. Install Pipeline: Stage View and the view appears on the job's main page.
The pipeline is Blue Ocean compatible. No special changes are needed—Blue Ocean renders Declarative Pipelines natively.
Blue Ocean features used:
- Stage visualization — Each stage appears as a step in the pipeline view
- input() for prod — Manual approval shows as a "Proceed" / "Abort" button in the UI
- Logs per stage — Click any stage to see its logs
Best practices for Blue Ocean:
- Use clear stage names (already in place)
- Keep stages focused (single responsibility)
- Use
inputfor gates (prod approval)
The pipeline deploys to the host for the selected environment and runs health checks there. On health failure, it rolls back to the previous build on the same host.
# Install Java 21
sudo dnf install -y java-21-openjdk
# Create deploy user with sudo
sudo useradd -m -s /bin/bash deploy
sudo usermod -aG wheel deploy
# Create app directory (in deploy home - no sudo needed)
mkdir -p /home/deploy/petclinicOn the Jenkins agent (RHEL/Alma):
# Generate key (if needed)
ssh-keygen -t ed25519 -N "" -f ~/.ssh/deploy_key
# Copy to Rocky Linux
ssh-copy-id -i ~/.ssh/deploy_key.pub deploy@192.168.31.121- Manage Jenkins → Credentials → Add
- Kind: SSH Username with private key
- ID:
deploy-ssh-key - Username:
deploy - Private Key: Enter directly — paste the contents of
deploy_key(or use From a file on Jenkins agent)
| Method | When to Use |
|---|---|
| jar | No registry. JAR is copied via SCP and run with java -jar. |
| docker | With registry: push image, remote pulls. Without: image transferred via docker save/scp/docker load. |
For Rocky Linux without a registry, use DEPLOY_METHOD = jar.
- Health URL:
http://192.168.31.121:8080/actuator/health - Health check runs after deploy. If it fails, the pipeline automatically rolls back to the previous build on 192.168.31.121.
- Clones this pipeline repo (Jenkinsfile, scripts)
- Clones Spring Petclinic into
app/ - Sets
BUILD_TAG=petclinic-{BUILD_NUMBER}-{GIT_SHORT_SHA}
- Checkstyle:
./mvnw checkstyle:check(built into Petclinic) - SonarQube: Optional; use
SKIP_SONAR=trueto bypass
- Runs
./mvnw testexcluding integration tests - Publishes JUnit XML reports
./mvnw clean package -DskipTests- Produces
app/target/spring-petclinic-4.0.0-SNAPSHOT.jar
- Copies JAR to
artifacts/ - Archives in Jenkins
- Stashes for potential rollback
- Multi-stage Dockerfile (Eclipse Temurin 21 JRE)
- Builds image:
{REGISTRY}/petclinic:{BUILD_TAG}
- Runs when
PUSH_TO_REGISTRY=true - Requires docker-registry-credentials in Jenkins
- Configure
DOCKER_REGISTRYin job environment
- Uses
jenkins/scripts/deploy.sh - Supports:
docker,docker-compose,kubectl - Set
DEPLOY_METHODenv var on agent
- Polls
HEALTH_CHECK_URLuntil healthy or timeout - Uses
jenkins/scripts/health-check.sh - Default:
http://localhost:8080/actuator/health
- If health check fails →
post { failure }runs - Uses Copy Artifact to get
deployed-image.txtfrom previous successful build - Runs
rollback.shwith previous image - Restores last known good deployment
| Variable | Description | Default |
|---|---|---|
REPO_URL |
Application repo | https://github.com/spring-projects/spring-petclinic.git |
BRANCH |
Branch to build | main |
DOCKER_REGISTRY |
Container registry host | registry.example.com |
APP_NAME |
Application name | petclinic |
DEPLOY_HOST |
Set per-env from branch | test: 192.168.31.121, prod: 192.168.31.124 |
DEPLOY_USER |
SSH user on deploy host | deploy |
REMOTE_APP_DIR |
App directory on remote | /home/deploy/petclinic |
HEALTH_CHECK_URL |
Smoke test URL (deploy host) | http://192.168.31.121:8080/actuator/health |
HEALTH_CHECK_TIMEOUT |
Health check timeout (seconds) | 120 |
| ID | Use |
|---|---|
docker-registry-credentials |
Registry login (username/password) |
github-credentials |
For private app repos (optional) |
- Run a successful build (Build #1)
- Run another build with FORCE_ROLLBACK_TEST =
true - Health check will fail → rollback deploys Build #1 image
pipelines/
├── Jenkinsfile # Main pipeline
├── README.md
├── SETUP_GUIDE.md # Complete setup guide
└── jenkins/
└── scripts/
├── deploy.sh # Deploy (Docker/Compose/K8s)
├── health-check.sh # Smoke test
└── rollback.sh # Rollback to previous build
# Install Docker
sudo dnf install -y docker
sudo systemctl enable --now docker
sudo usermod -aG docker jenkins # or the agent user
# Java 21
sudo dnf install -y java-21-openjdk-develIf Jenkins controller and agent are on different hosts:
- Deploy runs on the agent
- Health check runs on the agent
- Use
http://localhost:8080/actuator/healthwhen agent and container are on the same host - For remote agents, use
http://<agent-host>:8080/actuator/health
- Run full pipeline without SonarQube
- Add SonarQube and interpret quality gates
- Configure Docker registry and push images
- Deploy to dev via Docker
- Trigger rollback with FORCE_ROLLBACK_TEST
- Add Kubernetes deployment
- Add Slack/email notifications on failure
- Implement blue-green or canary deployment
MIT. Spring Petclinic is Apache 2.0.