Skip to content

Code Quality : SonarQube


Inspecting code continuously is a best practice in modern software development. The goal is to scan for defects at every production stage.

Quality Gate — A set of criteria that determines whether a project is ready to proceed to the next stage of the software lifecycle.


SonarQube is an open-source platform for continuous testing of code quality. It performs static code analysis and produces detailed reports on:

  • Bugs
  • Code smells
  • Vulnerabilities
  • Code duplications

Key facts:

  • Written in Java, but supports analysis of 20+ programming languages
  • Maintained by SonarSource
  • Extended by 50+ plugins
  • Language support varies depending on the edition in use
  1. Architecture and Design
  2. Duplications
  3. Unit Tests
  4. Potential Bugs
  5. Complexities
  6. Coding Standards / Rules
  7. Comments

SonarQube ingests and analyzes source files, then:

  1. Generates a set of metrics
  2. Stores them in a database
  3. Displays them on a dashboard

This recursive/continuous approach helps evaluate code quality and track how the codebase evolves over time. For example, you can view Coverage (~91.5%) and Unit Test counts per analysis run.

Note: Sonar has many capabilities typically configured by the dev/test team. The DevOps role is to spin up and integrate SonarQube. Implementing gates and rules is the responsibility of the dev/test team.


Detects and Alerts Automatically detects bugs and prompts developers to fix them before production. Also highlights complex code areas not covered by unit tests.

Sustainability Eliminates complexities, duplications, and potential defects. Ensures clean architecture and increases unit tests, extending the lifespan of applications.

Productivity Helps teams spot and eliminate code duplications and redundancy, reducing application size, complexity, maintenance time, and cost.

Raise Quality Provides multidimensional analysis across all 7 quality axes. Avoids duplicate code, keeps complexity low, detects violations, enforces coding standards, and documents APIs. Developers can build a customizable dashboard with filters.

Sharpen Developers’ Skills Provides regular feedback on quality issues, helping developers hone their programming skills and improve transparency in the code they write.

Scale With Business Needs Designed to scale without known limits. Tested at scale — analyzing over 5,000 projects with 4+ million lines of code and 20+ developers daily.

Enable Continuous Code Quality Management Makes code quality part of the development process itself. Ongoing monitoring increases software quality while reducing management costs and risks.


The SonarQube platform consists of four components:

image.png

  • One or more scanners running on your Build/CI servers
  • Responsible for analyzing project code

Runs three main processes:

  • Web Server — for developers and managers to browse quality snapshots and configure the instance
  • Search Server — based on Elasticsearch, backs UI searches
  • Compute Engine Server — processes code analysis reports and saves them to the SonarQube database

Multiple plugins installed on the server covering language support, SCM integration, authentication, and governance.

Stores:

  • Configuration of the SonarQube instance (security, plugins, settings)
  • Quality snapshots of projects, views, etc.

SonarQube integrates into the broader CI/CD pipeline as follows:

image.png

SonarLint
- One SonarQube Server starting three main processes:
- Developers code in their IDEs and use SonarLint to run a local analysis.
Manage Issues
- Developers use the SonarQube UI to manage and reduce their technical debt by reviewing, commenting on, and challenging their issues.
SCM
- Developers push their code into their favorite SCM: Git, SVN, TFVC, etc.
Automatic Build and Code Analysis
- SonarQube analysis requires the execution of the SonarScanner, which is triggered by the continuous integration server.
SonarQube Database
- The analysis report results are processed and stored in the SonarQube database, and the results are displayed in the UI by the SonarQube server.
Analysis Report
- The analysis report is sent to the SonarQube server for processing.
Reports
- Managers receive the results of the analysis in the form of reports. APIs are used by Ops to automate SonarQube configuration and data extraction. The SonarQube server is monitored by Ops using JMX.

7. Integration Constraints and Limitations

Section titled “7. Integration Constraints and Limitations”
  • Only one SonarQube server and one SonarQube database per platform (server can be installed in a cluster)
  • Each component (server, database, scanners) should be on its own dedicated machine for optimal performance
  • SonarScanners scale by adding machines
  • All machines must be time-synchronized
  • SonarQube Database and SonarQube Server must be on the same network
  • SonarQube Server and SonarScanners do not need to be on the same network
  • There is no direct communication between SonarScanners and the SonarQube database

Only hard prerequisite: Java (Oracle JRE 11 or OpenJDK 11)

Hardware Requirements:

  • Minimum 2GB RAM, with at least 1GB free RAM (for small-scale instances)
  • Disk space depends on the volume of code analyzed
  • Requires high read/write speed drives — especially for the data folder (Elasticsearch indices)
  • 32-bit systems are NOT supported on the server side (supported on scanner side)

Software Requirements:

  • Java: JVM versions 8 or 11 for scanners; version 11 required for the server
  • Databases (one of):
    • PostgreSQL 12, 11, 10, 9.3–9.6
    • Microsoft SQL Server 2017, 2016, 2014
    • Oracle 19C, 18C, 12C, 11G, XE Editions
  • Linux kernel settings:
    • vm.max_map_count >= 524288
    • fs.file-max >= 131072
    • User must be able to open at least 131072 file descriptors
    • User must be able to open at least 8192 threads
  • Fonts: fontconfig and a FreeType font package (e.g., libfreetype6) must be installed

seccomp filter note (Linux): Elasticsearch uses seccomp by default. If your distribution doesn’t support it (e.g., Red Hat Linux 6), disable it explicitly:

sonar.search.javaAdditionalOpts=-Dbootstrap.system_call_filter=false

Check seccomp availability with:

Terminal window
$ grep SECCOMP /boot/config-$(uname -r)

  1. Download and unzip the distribution
  2. Create a dedicated user account for SonarQube
  3. $SONARQUBE-HOME = path to the unzipped directory
  4. Edit $SONARQUBE-HOME/conf/sonar.properties to configure database settings
  5. For Oracle: copy the JDBC driver into $SONARQUBE-HOME/extensions/jdbc-driver/oracle
  6. Configure Elasticsearch storage paths:
sonar.path.data=/var/sonarqube/data
sonar.path.temp=/var/sonarqube/temp
  1. Start the web server (default port: 9000, default path: /):
    • Linux/Mac: bin/<OS>/sonar.sh start
    • Windows: bin/windows-x86-64/StartSonar.bat

Create volumes first to prevent data loss on upgrades:

Terminal window
docker volume create --name sonarqube_data
docker volume create --name sonarqube_extensions
docker volume create --name sonarqube_logs

Use volumes, not bind mounts. Bind mounts prevent plugins and languages from populating properly.

For Oracle databases, add the JDBC driver manually:

  1. Start the container with the embedded H2 database:
Terminal window
docker run --rm \\
-p 9000:9000 \\
-v sonarqube_extensions:/opt/sonarqube/extensions \\
<image_name>
  1. Exit once SonarQube has started
  2. Copy the Oracle driver into sonarqube_extensions/jdbc-driver/oracle

Run SonarQube with your database:

Terminal window
docker run -d --name sonarqube \\
-p 9000:9000 \\
-e SONAR_JDBC_URL=... \\
-e SONAR_JDBC_USERNAME=... \\
-e SONAR_JDBC_PASSWORD=... \\
-v sonarqube_data:/opt/sonarqube/data \\
-v sonarqube_extensions:/opt/sonarqube/extensions \\
-v sonarqube_logs:/opt/sonarqube/logs \\
<image_name>

Deprecated: SONARQUBE_JDBC_USERNAME, SONARQUBE_JDBC_PASSWORD, SONARQUBE_JDBC_URL — stop using these in new setups.


Option 1 — Marketplace (requires internet access)

Section titled “Option 1 — Marketplace (requires internet access)”
  1. Go to Administration > Marketplace
  2. Find the plugin and click Install
  3. Once downloaded, click Restart to apply

Option 2 — Manual Installation (no internet)

Section titled “Option 2 — Manual Installation (no internet)”
  1. Download the plugin .jar from its dedicated page (e.g., SonarPython)
  2. Replace any previous version in $SONARQUBE_HOME/extensions/plugins
  3. Restart the SonarQube server

SonarScanner is a client application that runs project analysis and sends results to the SonarQube server.

Create sonar-project.properties in your project root:

# Required — must be unique per SonarQube instance
sonar.projectKey=my:project
# Optional
#sonar.projectName=My project
#sonar.projectVersion=1.0
#sonar.sources=.
#sonar.sourceEncoding=UTF-8
  1. Unzip to a directory ($install_directory)
  2. Edit $install_directory/conf/sonar-scanner.properties to point to your server:
#sonar.host.url=http://localhost:9000
  1. Add $install_directory/bin to your PATH
  2. Verify installation:
Terminal window
sonar-scanner -h
  1. Run from your project’s base directory:
Terminal window
sonar-scanner
Terminal window
docker run \\
--rm \\
-e SONAR_HOST_URL="<http://$>{SONARQUBE_URL}" \\
-v "${YOUR_REPO}:/usr/src" \\
sonarsource/sonar-scanner-cli

Recommended default analyzer for Maven projects. Runs as a regular Maven goal — no separate runner setup needed.

Prerequisites: Maven 3.x, Java version supported by your SonarQube server

Global Settings (settings.xml at $MAVEN_HOME/conf or ~/.m2):

Section titled “Global Settings (settings.xml at $MAVEN_HOME/conf or ~/.m2):”
<settings>
<pluginGroups>
<pluginGroup>org.sonarsource.scanner.maven</pluginGroup>
</pluginGroups>
<profiles>
<profile>
<id>sonar</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<sonar.host.url><http://myserver:9000></sonar.host.url>
</properties>
</profile>
</profiles>
</settings>
Terminal window
mvn clean verify sonar:sonar

For multi-module projects:

Terminal window
mvn clean install
mvn sonar:sonar

To specify a plugin version explicitly:

Terminal window
mvn org.sonarsource.scanner.maven:sonar-maven-plugin:3.7.0.1746:sonar

Generate coverage reports before running analysis to include coverage data.

Overriding Analysis Properties (in pom.xml):

Section titled “Overriding Analysis Properties (in pom.xml):”
<properties>
<sonar.buildString> [...] </sonar.buildString>
</properties>

  • Install via Jenkins Update Center
  • Go to Manage Jenkins > Configure System
  • Add your SonarQube server details under the SonarQube configuration section
  • Create the server authentication token as a “Secret Text” credential

Global Configuration:

  1. Go to Manage Jenkins > Global Tool Configuration
  2. Click Add SonarScanner for MSBuild
  3. Enable Install automatically to auto-install on Jenkins executors

Job Configuration:

  1. In Build section, add SonarQube for MSBuild - Begin Analysis
  2. Set Project Key, Name, and Version
  3. Add MSBuild build step
  4. Add SonarQube for MSBuild - End Analysis

Global Configuration:

  1. Go to Manage Jenkins > Configure System
  2. In SonarQube servers section, check Enable injection of SonarQube server configuration as build environment variables

Job Configuration:

  1. In Build Environment, enable Prepare the SonarScanner environment
  2. Use injected environment variables in your build steps:
    • Maven goal: $SONAR_MAVEN_GOAL
    • Gradle task: sonarqube

Use the withSonarQubeEnv step to select and connect to your SonarQube server.

MSBuild Example:

node {
stage('SCM') {
git '<https://github.com/foo/bar.git>'
}
stage('Build + SonarQube analysis') {
def sqScannerMsBuildHome = tool 'Scanner for MSBuild 4.6'
withSonarQubeEnv('My SonarQube Server') {
bat "${sqScannerMsBuildHome}\\\\SonarQube.Scanner.MSBuild.exe begin /k:myKey"
bat 'MSBuild.exe /t:Rebuild'
bat "${sqScannerMsBuildHome}\\\\SonarQube.Scanner.MSBuild.exe end"
}
}
}

Replace bat with sh for Unix-based Jenkins slaves.

SonarScanner Example:

node {
stage('SCM') {
git 'https://github.com/foo/bar.git'
}
stage('SonarQube analysis') {
def scannerHome = tool 'SonarScanner 4.0'
withSonarQubeEnv('My SonarQube Server') {
sh "${scannerHome}/bin/sonar-scanner"
}
}
}

SonarScanner for Gradle Example:

node {
stage('SCM') {
git 'https://github.com/foo/bar.git'
}
stage('SonarQube analysis') {
withSonarQubeEnv() { // Picks the global server connection configured in Jenkins
sh './gradlew sonarqube'
}
}
}

SonarScanner for Maven Example:

node {
stage('SCM') {
git 'https://github.com/foo/bar.git'
}
stage('SonarQube analysis') {
withSonarQubeEnv(credentialsId: 'f225455e-ea59-40fa-8af7-08176e86507a', installationName: 'My SonarQube Server') {
sh 'mvn org.sonarsource.scanner.maven:sonar-maven-plugin:3.7.0.1746:sonar'
}
}
}

13. Pausing Pipeline Until Quality Gate Is Computed

Section titled “13. Pausing Pipeline Until Quality Gate Is Computed”

The waitForQualityGate step holds the pipeline until SonarQube finishes analysis and returns the quality gate status.

  • Configure a webhook in SonarQube pointing to <your Jenkins instance>/sonarqube-webhook/
  • Use withSonarQubeEnv in your pipeline so the SonarQube taskId is correctly attached to the pipeline context

The step is lightweight — it does not occupy a node for polling and survives Jenkins restarts. To avoid race conditions, it makes a direct call to the server on start (or restart) to check if the task already completed.

pipeline {
agent any
stages {
stage('SCM') {
steps {
git url: 'https://github.com/foo/bar.git'
}
}
stage('Build && SonarQube analysis') {
steps {
withSonarQubeEnv('My SonarQube Server') {
withMaven(maven:'Maven 3.5') {
sh 'mvn clean package sonar:sonar'
}
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 1, unit: 'HOURS') {
// abortPipeline: true → fails the pipeline if Quality Gate fails
// abortPipeline: false → marks pipeline UNSTABLE instead
waitForQualityGate abortPipeline: true
}
}
}
}
}

When running multiple analyses, alternate each analysis stage with its own Quality Gate stage in order:

pipeline {
agent any
stages {
stage('SonarQube analysis 1') {
steps {
sh 'mvn clean package sonar:sonar'
}
}
stage('Quality Gate 1') {
steps {
waitForQualityGate abortPipeline: true
}
}
stage('SonarQube analysis 2') {
steps {
sh 'gradle sonarqube'
}
}
stage('Quality Gate 2') {
steps {
waitForQualityGate abortPipeline: true
}
}
}
}

You can add a secret to your SonarQube webhook to verify the payload sent to Jenkins.

To use a webhook secret at the project level, add it to Jenkins and call waitForQualityGate with the secret ID:

// Declarative pipeline
waitForQualityGate(webhookSecretId: 'yourSecretID')
// Scripted pipeline
waitForQualityGate webhookSecretId: 'yourSecretID'

14. Jenkins Integration — Additional Notes

Section titled “14. Jenkins Integration — Additional Notes”

SonarScanners running in Jenkins can automatically detect branches, merge requests, and pull requests — no need to pass branch details explicitly.

  • Bitbucket, GitHub, or GitLab branch source plugin (depending on your ALM)
  • SonarQube Scanner plugin
  • Community Edition supports analyzing one branch only
  • Available from Developer Edition onwards
  • Environment variables in multibranch pipeline jobs are used to auto-configure branch and pull request details
  • Jenkins plugins export the information needed to set these variables

SonarScanners need access to the target branch to detect changed code in a pull request. If your Jenkins pull request discovery strategy only fetches the PR (without merging it), the target branch won’t be available locally, and you may see this warning in scanner logs:

File '[name]' was detected as changed but without having changed lines

Fix this by changing the discovery strategy or manually fetching the target branch before running SonarScanner:

Terminal window
git fetch +refs/heads/${CHANGE_TARGET}:refs/remotes/origin/${CHANGE_TARGET}