Jenkins SonarQube Integration: 3 Ways to Trigger Code Scans in CI/CD Pipelines
This tutorial covers the theory and three practical methods for triggering SonarQube code scans from Jenkins — Freestyle jobs, Pipeline scripts, and Shared Libraries — using a Maven project example in a Docker Compose environment with detailed configuration steps.
Theoretical Foundation: Why Jenkins Triggers SonarQube
In a CI pipeline, Jenkins acts as the scheduling hub deciding when and which code to scan, while SonarQube handles the analysis — detecting bugs, vulnerabilities, code smells, test coverage, duplication, complexity, and evaluating Quality Gates.
Jenkins decides scan timing; SonarQube decides scan content and standards.
Three Common Trigger Methods
SonarScanner CLI — Install sonar-scanner in the Jenkins container; run commands like:
sonar-scanner \
-Dsonar.projectKey=k8s-dashboard \
-Dsonar.projectName=k8s-dashboard \
-Dsonar.sources=src \
-Dsonar.java.binaries=target \
-Dsonar.host.url=http://sonarqube:9000 \
-Dsonar.login=$SONAR_TOKENWorks for any language; flexible configuration.
Maven Sonar Plugin (for Java/Maven) — Use sonar-maven-plugin; append to Maven phase:
mvn clean verify sonar:sonar \
-DskipTests=false \
-Dsonar.projectKey=k8s-dashboard \
-Dsonar.host.url=http://sonarqube:9000 \
-Dsonar.login=$SONAR_TOKENJenkins only runs mvn; plugin pushes results automatically.
Jenkins SonarQube Scanner Plugin + withSonarQubeEnv — Configure SonarQube server and credentials in Jenkins global settings; in Pipeline:
withSonarQubeEnv('sonarqube') {
sh 'mvn clean verify sonar:sonar'
} withSonarQubeEnvinjects SONAR_HOST_URL and SONAR_AUTH_TOKEN automatically.
Docker Compose Jenkins Environment
The article uses docker-compose with jenkins/jenkins:lts-jdk17. Tools (JDK, Maven, Git, Node) are mounted from host directories into the container. Example docker-compose.yaml snippet:
version: '3.8'
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
container_name: jenkins
user: '1000:993'
ports:
- '8080:8080'
- '50000:50000'
volumes:
- ./jenkins_data:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
- ./tools/jdk:/opt/tools/jdk:ro
- ./tools/maven:/opt/tools/maven:ro
- ./tools/git:/opt/tools/git:ro
- ./tools/node:/opt/tools/node:ro
- ./casc:/var/jenkins_casc:ro
environment:
- JAVA_OPTS=-Duser.timezone=Asia/Shanghai
- JENKINS_OPTS=--prefix=/jenkins
networks:
- ci-network
restart: unless-stopped
networks:
ci-network:
driver: bridgeAll build steps (e.g., mvn, sonar-scanner) execute inside the Jenkins container. To troubleshoot, run docker exec -it jenkins bash.
Making sonar-scanner Available in the Container
Two approaches:
Option 1: Mount host's existing /usr/local/sonar-scanner as /opt/tools/sonar-scanner:ro in docker-compose.yaml, then verify with
docker exec -it jenkins bash -lc '/opt/tools/sonar-scanner/bin/sonar-scanner -v'.
Option 2: Download a specific version into ./tools/sonar-scanner/current on host, then mount that directory. Commands:
mkdir -p ./tools/sonar-scanner
cd ./tools/sonar-scanner
curl -fsSLO https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-<version>-linux-x64.zip
unzip -o sonar-scanner-cli-<version>-linux-x64.zip
mv sonar-scanner-<version>-linux-x64 current
chmod -R a+rX ./currentThen mount ./tools/sonar-scanner/current:/opt/tools/sonar-scanner:ro.
For Freestyle shell steps, use a fixed path:
SONAR_SCANNER_HOME=/opt/tools/sonar-scanner; ${SONAR_SCANNER_HOME}/bin/sonar-scanner -v.
SonarQube Pre-Configuration
Create a project (e.g., projectKey=k8s-dashboard).
Generate a CI token: User → My Account → Security → Generate Token (e.g., jenkins-k8s-dashboard). Example token: sqp_99a9e261e0bfce5f58f037e95171209a84401e0d.
Store token in Jenkins as Secret text credential with ID sonarqube-token.
Jenkins Global Configuration
System Configuration → SonarQube servers :
Name: sonarqube Server URL: http://192.168.145.129:9000 (or http://sonarqube:9000 if on same Docker network)
Authentication token: select sonarqube-token credential
Global Tool Configuration → SonarQube Scanner :
Name: sonar-scanner Installation: Fixed local SonarScanner (not auto-install)
Local path: /opt/tools/sonar-scanner Required plugins: SonarQube Scanner for Jenkins (provides withSonarQubeEnv and waitForQualityGate), Pipeline Maven Integration (optional), Credentials Binding (for Freestyle).
Practice 1: Freestyle Job
Suitable for demos, simple or one-off projects. Steps:
New Freestyle job named k8s-dashboard-sonar-freestyle.
Source Code Management: Git, repo ssh://[email protected]:2222/root/k8s-dashboard.git, branch main, credentials gitlab-ssh-cred.
Build Environment: Enable Credentials Binding → Secret text → Variable SONAR_TOKEN, Credential sonarqube-token.
Build Step 1: Invoke top-level Maven targets → Maven version (e.g., Maven3.9.10) → Goals: clean verify -DskipTests=false (runs tests for coverage).
Build Step 2: Execute shell — run SonarScanner CLI with variables:
echo "===== Current dir & build output ====="
pwd
ls -lh target
test -d target/classes
SONAR_SCANNER_HOME=/opt/tools/sonar-scanner
${SONAR_SCANNER_HOME}/bin/sonar-scanner -v
SONAR_HOST_URL="http://192.168.145.129:9000"
SONAR_PROJECT_KEY="k8s-dashboard"
SONAR_PROJECT_NAME="k8s-dashboard"
${SONAR_SCANNER_HOME}/bin/sonar-scanner \
-Dsonar.projectKey=${SONAR_PROJECT_KEY} \
-Dsonar.projectName=${SONAR_PROJECT_NAME} \
-Dsonar.sources=src \
-Dsonar.java.binaries=target/classes \
-Dsonar.host.url=${SONAR_HOST_URL} \
-Dsonar.login=${SONAR_TOKEN}Trigger build; watch console for INFO: ANALYSIS SUCCESSFUL. View results in SonarQube project dashboard.
Practice 2: Pipeline Job (Recommended)
Pipeline stores Jenkinsfile in SCM, enabling version control and team collaboration. Example Jenkinsfile combining Maven Sonar Plugin + withSonarQubeEnv:
pipeline {
agent any
tools {
jdk 'JDK11'
maven 'Maven3.9.10'
}
environment {
GIT_URL = 'ssh://[email protected]:2222/root/k8s-dashboard.git'
BRANCH_NAME = 'main'
IMAGE_TAG = ''
SONARQUBE_SERVER = 'sonarqube'
SONAR_PROJECT_KEY = 'k8s-dashboard'
}
stages {
stage('📥 Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "refs/heads/${BRANCH_NAME}"]],
userRemoteConfigs: [[url: "${GIT_URL}", credentialsId: 'gitlab-ssh-cred']],
extensions: [
[$class: 'CheckoutOption', timeout: 30],
[$class: 'LocalBranch', localBranch: "${BRANCH_NAME}"],
[$class: 'CloneOption', depth: 1, noTags: false, reference: '', timeout: 10]
]
])
script {
env.IMAGE_TAG = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
}
sh '''
git config --list | grep remote.origin.url
git branch -v
git status
'''
}
}
stage('🔨 Maven Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('🔍 SonarQube Scan') {
steps {
withSonarQubeEnv("${SONARQUBE_SERVER}") {
sh """
mvn sonar:sonar \
-Dsonar.projectKey=${SONAR_PROJECT_KEY} \
-Dsonar.projectName=${SONAR_PROJECT_KEY} \
-Dsonar.host.url=$SONAR_HOST_URL \
-Dsonar.login=$SONAR_AUTH_TOKEN
"""
}
}
}
stage('⏱ Quality Gate Check (optional)') {
steps {
timeout(time: 2, unit: 'MINUTES') {
script {
// Requires SonarQube Scanner for Jenkins plugin and Webhook configured in SonarQube
waitForQualityGate abortPipeline: true
}
}
}
}
// Subsequent deploy stages (Nexus, Harbor) commented out for this experiment
}
post {
success {
echo "✅ SonarQube scan succeeded: project ${SONAR_PROJECT_KEY}, commit ${IMAGE_TAG}"
}
failure {
echo "❌ Build or scan failed, see log: ${env.BUILD_URL}console"
}
}
}Key Points
withSonarQubeEnv('sonarqube')name must match global config.
It injects SONAR_HOST_URL and SONAR_AUTH_TOKEN. IMAGE_TAG is set after checkout via script { env.IMAGE_TAG = sh(...).trim() } — not in environment block because repo isn't cloned yet. waitForQualityGate needs the plugin and a SonarQube Webhook pointing to http://192.168.145.129:8080/jenkins/sonarqube-webhook/ (due to JENKINS_OPTS=--prefix=/jenkins). Fails pipeline if gate fails.
Deploy stages (Nexus, Docker) should come after successful Quality Gate.
Docker Compose Networking Notes
If Jenkins can't resolve sonarqube hostname, put both services in same ci-network or use fixed IP in global config.
If mvn not found, ensure Maven is installed in Jenkins image or via Dockerfile.
Practice 3: Shared Library for Enterprise Standardization
When multiple projects need Sonar scans, centralize logic in a Jenkins Shared Library ( my-shared-lib@main, repo
ssh://[email protected]:2222/root/jenkins-shared-library.git).
1. Create vars/sonarScan.groovy
/**
* Unified SonarQube scan function
* @param config Map with:
* projectKey - required
* projectName - defaults to projectKey
* sonarServer - defaults to 'sonarqube'
* mavenGoals - defaults to 'clean verify'
* extraOptions - optional extra sonar params
*/
def call(Map config = [:]) {
def projectKey = config.projectKey
def projectName = config.projectName ?: projectKey
def sonarServer = config.sonarServer ?: 'sonarqube'
def mavenGoals = config.mavenGoals ?: 'clean verify'
def extraOptions = config.extraOptions ?: ''
if (!projectKey) {
error 'sonarScan: projectKey is required'
}
stage("SonarQube Scan - ${projectKey}") {
withSonarQubeEnv(sonarServer) {
sh """
mvn ${mavenGoals} sonar:sonar \
-Dsonar.projectKey=${projectKey} \
-Dsonar.projectName=${projectName} \
-Dsonar.host.url=$SONAR_HOST_URL \
-Dsonar.login=$SONAR_AUTH_TOKEN ${extraOptions}
"""
}
}
}Note: This function defaults to Maven Sonar Plugin, which is simpler for Maven projects than calling sonar-scanner directly.
2. Configure Shared Library in Jenkins
Manage Jenkins → System Configuration → Global Pipeline Libraries
Name: my-shared-lib, Default version: main, Retrieval: Modern SCM → Git → URL
ssh://[email protected]:2222/root/jenkins-shared-library.git, Credentials: gitlab-ssh-cred.
3. Project Jenkinsfile Using Shared Library
@Library('my-shared-lib@main') _
pipeline {
agent any
tools {
jdk 'JDK11'
maven 'Maven3.9.10'
}
environment {
GIT_URL = 'ssh://[email protected]:2222/root/k8s-dashboard.git'
BRANCH_NAME = 'main'
SONAR_PROJECT_KEY = 'k8s-dashboard'
}
stages {
stage('📥 Standard Checkout') {
steps {
script {
gitCheckout(
repoUrl: GIT_URL,
branch: BRANCH_NAME,
credentialsId: 'gitlab-ssh-cred',
depth: 1
)
}
}
}
stage('🔨 Standard Maven Build') {
steps {
script {
mavenBuild(goals: 'clean verify', skipTests: false)
}
}
}
stage('🔍 Call Shared Library Sonar Scan') {
steps {
script {
sonarScan(
projectKey: SONAR_PROJECT_KEY,
projectName: 'k8s-dashboard',
sonarServer: 'sonarqube',
mavenGoals: 'clean verify'
)
}
}
}
stage('⏱ Quality Gate') {
steps {
timeout(time: 2, unit: 'MINUTES') {
script {
waitForQualityGate abortPipeline: true
}
}
}
}
}
post {
success { echo """...""" }
failure { echo """...""" }
}
}Benefits of Shared Library
Unified standards — all projects use same scan logic.
Centralized upgrades — change URL, auth, parameters in one place ( sonarScan.groovy).
Lower barrier — project Jenkinsfile stays concise, only business-specific config.
Summary & Selection Guide
Freestyle + sonar-scanner
Best for: Demos, temporary, single-project
Pros: Simple, quick start
Cons: Logic scattered in UI, hard to reuse
Pipeline + Maven Sonar Plugin
Best for: Team projects, version-controlled pipelines
Pros: Pipeline-as-code, auditable, rollbackable
Cons: Requires Jenkinsfile & plugin knowledge
Shared Library ( my-shared-lib@main )
Best for: Multi-project reuse, enterprise standardization
Pros: Single maintenance, consistent, clean project Jenkinsfiles
Cons: Needs shared library repo & version management
You now have a stable, Docker-Compose-based Jenkins setup that triggers SonarQube scans for Java projects like k8s-dashboard, establishing a visible, traceable, enforceable code-quality baseline. Next steps: bind Quality Gate to deployment (no-pass-no-deploy), push results to IM (DingTalk, Lark), and extend shared library with coverage thresholds, hotfix branch policies, etc.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Cilantro Loves Linux
Focused on Linux system operations and maintenance technology exchange, regularly publishing in-depth technical articles covering core areas such as system architecture, performance optimization, and security hardening. Whether you are a beginner or a seasoned expert, you can find valuable resources to enhance your skills and solve problems here. Follow us to explore the mysteries of Linux operations!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
