Set up SonarQube quality gates with custom conditions, create language-specific rules, and integrate automated quality enforcement into CI/CD pipelines for continuous code quality monitoring.
Prerequisites
- SonarQube server running
- Administrative access to SonarQube
- CI/CD pipeline access
- Basic understanding of code quality metrics
What this solves
SonarQube quality gates enforce code quality standards by blocking deployments when code doesn't meet defined criteria. This tutorial shows you how to configure custom quality gates with specific conditions, create custom rules for different programming languages, and integrate quality enforcement into CI/CD pipelines. You'll also set up monitoring and alerting for code quality metrics.
Step-by-step configuration
Install SonarQube scanner CLI tools
Install the SonarQube scanner on your development and CI/CD machines to analyze code and send results to your SonarQube server.
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip
sudo unzip sonar-scanner-cli-5.0.1.3006-linux.zip -d /opt/
sudo mv /opt/sonar-scanner-5.0.1.3006-linux /opt/sonar-scanner
sudo ln -s /opt/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner
Configure SonarQube scanner properties
Set up the scanner configuration to connect to your SonarQube server and define project settings.
# SonarQube server configuration
sonar.host.url=https://sonarqube.example.com
sonar.login=your-auth-token
Global analysis properties
sonar.sourceEncoding=UTF-8
sonar.exclusions=/test/,/*.spec.js,/node_modules/,/vendor/**
sonar.coverage.exclusions=/test/,**/*.spec.js
Language-specific settings
sonar.java.binaries=target/classes
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.python.coverage.reportPaths=coverage.xml
Create custom quality gate
Access your SonarQube web interface and create a custom quality gate with specific conditions for your projects.
Navigate to Quality Gates > Create and configure the following conditions:
# Coverage conditions
Coverage on New Code: is less than 80%
Line Coverage on New Code: is less than 80%
Branch Coverage on New Code: is less than 70%
Reliability conditions
Reliability Rating on New Code: is worse than A
Bugs on New Code: is greater than 0
Security conditions
Security Rating on New Code: is worse than A
Security Hotspots Reviewed on New Code: is less than 100%
Vulnerabilities on New Code: is greater than 0
Maintainability conditions
Maintainability Rating on New Code: is worse than A
Code Smells on New Code: is greater than 5
Technical Debt on New Code: is greater than 1h
Duplications
Duplicated Lines (%) on New Code: is greater than 3%
Configure custom rules for Java
Create custom rules specific to Java development standards in your organization.
# Create custom rule profile
Navigate to Rules > Create Custom Rule
Select Java language and template
Example: Method complexity rule
Rule Key: custom-java-method-complexity
Name: Method Cyclomatic Complexity
Description: Methods should not be too complex
Type: Code Smell
Severity: Major
Template: Avoid too complex method
Parameters:
- Complexity Threshold: 10
- Message: Method complexity is {0}, max allowed is {1}
Example: Class naming convention
Rule Key: custom-java-class-naming
Name: Class Naming Convention
Description: Classes must follow naming standards
Type: Code Smell
Severity: Minor
Template: Track usage of a class
Parameters:
- Pattern: ^[A-Z][a-zA-Z0-9]*$
- Message: Class name should be PascalCase
Configure custom rules for JavaScript/TypeScript
Set up JavaScript and TypeScript specific rules for modern development practices.
# ESLint integration rules
Navigate to Rules > JavaScript/TypeScript
Console usage rule
Rule Key: custom-js-no-console
Name: No Console Statements
Description: Console statements should not be used in production
Type: Code Smell
Severity: Minor
ESLint Rule: no-console
Function complexity rule
Rule Key: custom-js-function-complexity
Name: Function Complexity Limit
Description: Functions should not exceed complexity threshold
Type: Code Smell
Severity: Major
ESLint Rule: complexity
Parameters:
- max: 8
Unused variables rule
Rule Key: custom-js-no-unused-vars
Name: No Unused Variables
Description: Variables should be used once declared
Type: Bug
Severity: Major
ESLint Rule: no-unused-vars
Parameters:
- vars: all
- args: after-used
Configure custom rules for Python
Create Python-specific rules for code quality and security best practices.
# Python custom rules using Pylint integration
Navigate to Rules > Python
Function length rule
Rule Key: custom-python-function-length
Name: Function Length Limit
Description: Functions should not exceed line limit
Type: Code Smell
Severity: Major
Pylint Rule: too-many-lines
Parameters:
- max-lines: 50
Import organization rule
Rule Key: custom-python-import-order
Name: Import Order Standard
Description: Imports should follow PEP8 conventions
Type: Code Smell
Severity: Minor
Pylint Rule: wrong-import-order
Security rule for hardcoded passwords
Rule Key: custom-python-hardcoded-password
Name: No Hardcoded Passwords
Description: Passwords should not be hardcoded
Type: Vulnerability
Severity: Blocker
Bandit Rule: B105
Create quality profile with custom rules
Combine built-in and custom rules into a quality profile that applies to your projects.
# Create new quality profile
Navigate to Quality Profiles > Create
Profile Name: Enterprise Standards
Language: Java
Parent Profile: Sonar way
Activate custom rules
Activated Rules:
- All Sonar way rules (inherited)
- custom-java-method-complexity (Major)
- custom-java-class-naming (Minor)
- Unused private method (Major)
- Magic numbers should not be used (Minor)
- Methods should not have too many parameters (Major)
Severity adjustments
Rule Modifications:
- Cognitive Complexity: Blocker (was Major)
- Public classes should have package info: Info (was Minor)
- Missing JavaDoc: Minor (was Major)
Configure project-specific analysis settings
Set up project configuration file for automated analysis with your custom rules and quality gate.
# Project identification
sonar.projectKey=my-application
sonar.projectName=My Application
sonar.projectVersion=1.0
Source configuration
sonar.sources=src/main
sonar.tests=src/test
sonar.java.source=17
sonar.java.target=17
Quality profile assignment
sonar.profile=Enterprise Standards
Quality gate assignment
sonar.qualitygate=Enterprise Quality Gate
Coverage settings
sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
sonar.junit.reportPaths=target/surefire-reports
Analysis parameters
sonar.analysis.mode=publish
sonar.scm.provider=git
sonar.links.homepage=https://github.com/company/my-application
sonar.links.ci=https://jenkins.company.com/job/my-application
Set up GitLab CI integration
Configure GitLab CI pipeline to run SonarQube analysis and enforce quality gate status.
stages:
- test
- quality-check
- deploy
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: "0"
sonarqube-check:
stage: quality-check
image: sonarsource/sonar-scanner-cli:latest
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- sonar-scanner
-Dsonar.projectKey=${CI_PROJECT_NAME}
-Dsonar.sources=.
-Dsonar.host.url=${SONAR_HOST_URL}
-Dsonar.login=${SONAR_TOKEN}
-Dsonar.gitlab.project_id=${CI_PROJECT_ID}
-Dsonar.gitlab.commit_sha=${CI_COMMIT_SHA}
-Dsonar.gitlab.ref_name=${CI_COMMIT_REF_NAME}
only:
- merge_requests
- main
- develop
quality-gate-check:
stage: quality-check
image: curlimages/curl:latest
script:
- |
# Wait for quality gate result
sleep 30
TASK_ID=$(curl -s -u ${SONAR_TOKEN}: "${SONAR_HOST_URL}/api/ce/component?component=${CI_PROJECT_NAME}" | jq -r '.queue[0].id // .current.id')
# Check task status
while true; do
STATUS=$(curl -s -u ${SONAR_TOKEN}: "${SONAR_HOST_URL}/api/ce/task?id=${TASK_ID}" | jq -r '.task.status')
if [ "$STATUS" = "SUCCESS" ]; then
break
elif [ "$STATUS" = "FAILED" ]; then
echo "SonarQube analysis failed"
exit 1
fi
sleep 10
done
# Check quality gate status
QG_STATUS=$(curl -s -u ${SONAR_TOKEN}: "${SONAR_HOST_URL}/api/qualitygates/project_status?projectKey=${CI_PROJECT_NAME}" | jq -r '.projectStatus.status')
if [ "$QG_STATUS" != "OK" ]; then
echo "Quality gate failed: $QG_STATUS"
exit 1
fi
echo "Quality gate passed"
dependencies:
- sonarqube-check
only:
- merge_requests
- main
- develop
Configure Jenkins integration
Set up Jenkins pipeline for SonarQube analysis with quality gate enforcement.
pipeline {
agent any
tools {
maven 'Maven-3.9'
jdk 'JDK-17'
}
environment {
SONAR_TOKEN = credentials('sonar-token')
SONAR_HOST_URL = 'https://sonarqube.example.com'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build and Test') {
steps {
sh 'mvn clean compile test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
publishCoverage adapters: [jacocoAdapter('target/site/jacoco/jacoco.xml')]
}
}
}
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('SonarQube') {
sh '''
mvn sonar:sonar \
-Dsonar.projectKey=${JOB_NAME} \
-Dsonar.host.url=${SONAR_HOST_URL} \
-Dsonar.login=${SONAR_TOKEN} \
-Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
'''
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
stage('Deploy') {
when {
anyOf {
branch 'main'
branch 'develop'
}
}
steps {
echo 'Deploying application...'
// Add deployment steps here
}
}
}
post {
failure {
emailext (
subject: "Quality Gate Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Quality gate failed for ${env.BUILD_URL}",
to: "${env.CHANGE_AUTHOR_EMAIL}"
)
}
}
}
Set up webhook notifications
Configure SonarQube webhooks to send quality gate results to external systems like Slack or Microsoft Teams.
# Navigate to Administration > Configuration > Webhooks
Create webhook for Slack integration
Name: Slack Quality Gate Notifications
URL: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
Secret: your-webhook-secret
Webhook payload template (JSON)
{
"text": "Quality Gate Status: {{status}}",
"attachments": [
{
"color": "{{#eq status 'OK'}}good{{else}}danger{{/eq}}",
"fields": [
{
"title": "Project",
"value": "{{project.name}}",
"short": true
},
{
"title": "Branch",
"value": "{{branch.name}}",
"short": true
},
{
"title": "Quality Gate",
"value": "{{qualityGate.name}}",
"short": true
},
{
"title": "Analysis",
"value": "<{{serverUrl}}/dashboard?id={{project.key}}|View Details>",
"short": true
}
]
}
]
}
Configure quality gate metrics monitoring
Set up monitoring dashboards and alerts for code quality metrics across projects.
# Install SonarQube Prometheus exporter
wget https://github.com/dmeiners88/sonarqube-prometheus-exporter/releases/download/v1.3.0/sonar-exporter-1.3.0.jar
Create systemd service
sudo tee /etc/systemd/system/sonar-exporter.service << EOF
[Unit]
Description=SonarQube Prometheus Exporter
After=network.target
[Service]
Type=simple
User=sonar-exporter
Group=sonar-exporter
ExecStart=/usr/bin/java -jar /opt/sonar-exporter/sonar-exporter-1.3.0.jar \
--sonar.host.url=https://sonarqube.example.com \
--sonar.login=your-monitoring-token \
--web.listen-address=:9765
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
Create user and directories
sudo useradd -r -s /bin/false sonar-exporter
sudo mkdir -p /opt/sonar-exporter
sudo mv sonar-exporter-1.3.0.jar /opt/sonar-exporter/
sudo chown -R sonar-exporter:sonar-exporter /opt/sonar-exporter
Start service
sudo systemctl enable --now sonar-exporter
Create Grafana dashboard for code quality
Build comprehensive dashboards to visualize code quality trends and quality gate status across projects.
{
"dashboard": {
"id": null,
"title": "SonarQube Code Quality Dashboard",
"tags": ["sonarqube", "quality"],
"timezone": "browser",
"panels": [
{
"title": "Quality Gate Status",
"type": "stat",
"targets": [
{
"expr": "sonar_quality_gate_status",
"legendFormat": "{{project_name}}"
}
],
"fieldConfig": {
"defaults": {
"mappings": [
{"options": {"0": {"text": "ERROR", "color": "red"}}},
{"options": {"1": {"text": "OK", "color": "green"}}}
]
}
}
},
{
"title": "Code Coverage Trend",
"type": "graph",
"targets": [
{
"expr": "sonar_coverage_line_coverage",
"legendFormat": "{{project_name}}"
}
]
},
{
"title": "Technical Debt",
"type": "graph",
"targets": [
{
"expr": "sonar_technical_debt_minutes",
"legendFormat": "{{project_name}}"
}
]
},
{
"title": "Security Vulnerabilities",
"type": "table",
"targets": [
{
"expr": "sonar_vulnerabilities{severity=~\"BLOCKER|CRITICAL|MAJOR\"}",
"format": "table",
"instant": true
}
]
}
]
}
}
Verify your setup
# Test SonarQube scanner
sonar-scanner --version
Run analysis on sample project
cd /path/to/your/project
sonar-scanner
Check quality gate API
curl -u your-token: "https://sonarqube.example.com/api/qualitygates/project_status?projectKey=your-project"
Verify webhook delivery
curl -X POST https://sonarqube.example.com/api/webhooks/deliveries \
-u your-token: \
-H "Content-Type: application/json"
Test Prometheus exporter
curl http://localhost:9765/metrics | grep sonar
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Quality gate always passes | Conditions not properly configured | Check condition thresholds in Quality Gates > Your Gate > Conditions |
| Custom rules not applied | Quality profile not assigned to project | Go to Project Settings > Quality Profiles and assign custom profile |
| Analysis fails with permission error | Scanner token lacks analysis permissions | Generate new token with Execute Analysis permission in SonarQube |
| Coverage reports not found | Incorrect coverage file paths | Verify coverage report paths match actual file locations |
| Webhook not triggered | Webhook URL unreachable or invalid | Test webhook URL manually and check SonarQube logs |
| CI pipeline hangs on quality gate | Quality gate check timeout | Increase timeout or check SonarQube background task queue |
Next steps
- Configure SonarQube high availability clustering with PostgreSQL and load balancing
- Configure SonarQube LDAP authentication with Active Directory and user group management
- Set up SonarQube branch analysis with pull request decoration for enhanced code quality workflows
- Integrate SonarQube with Kubernetes security scanning workflows for continuous code quality analysis
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m'
# Configuration
readonly SONAR_VERSION="5.0.1.3006"
readonly SONAR_SCANNER_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_VERSION}-linux.zip"
readonly INSTALL_DIR="/opt/sonar-scanner"
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Usage message
usage() {
echo "Usage: $0 [SONAR_HOST_URL] [SONAR_TOKEN]"
echo "Example: $0 https://sonarqube.example.com squ_abc123..."
echo ""
echo "Arguments:"
echo " SONAR_HOST_URL - SonarQube server URL (optional)"
echo " SONAR_TOKEN - SonarQube authentication token (optional)"
exit 1
}
# Cleanup function for rollback
cleanup() {
local exit_code=$?
if [ $exit_code -ne 0 ]; then
print_error "Installation failed. Cleaning up..."
sudo rm -rf "${INSTALL_DIR}" 2>/dev/null || true
sudo rm -f /usr/local/bin/sonar-scanner 2>/dev/null || true
sudo rm -f /tmp/sonar-scanner-cli-*.zip 2>/dev/null || true
fi
exit $exit_code
}
trap cleanup ERR
# Check if running as root or with sudo
check_privileges() {
if [[ $EUID -eq 0 ]]; then
print_warning "Running as root. Consider using a non-root user with sudo."
elif ! sudo -n true 2>/dev/null; then
print_error "This script requires sudo privileges"
exit 1
fi
}
# Detect distribution and set package manager
detect_distro() {
if [ ! -f /etc/os-release ]; then
print_error "Cannot detect Linux distribution"
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf check-update || true"
;;
fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf check-update || true"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum check-update || true"
;;
*)
print_error "Unsupported distribution: $ID"
exit 1
;;
esac
print_status "Detected distribution: $ID using $PKG_MGR"
}
# Install required packages
install_dependencies() {
print_status "[1/7] Installing dependencies..."
sudo $PKG_UPDATE
case "$PKG_MGR" in
apt)
sudo $PKG_INSTALL wget unzip openjdk-17-jre-headless curl
;;
dnf|yum)
sudo $PKG_INSTALL wget unzip java-17-openjdk-headless curl
;;
esac
}
# Download and install SonarQube Scanner
install_sonar_scanner() {
print_status "[2/7] Downloading SonarQube Scanner..."
cd /tmp
wget -q "$SONAR_SCANNER_URL" -O "sonar-scanner-cli-${SONAR_VERSION}-linux.zip"
print_status "[3/7] Installing SonarQube Scanner..."
sudo unzip -q "sonar-scanner-cli-${SONAR_VERSION}-linux.zip" -d /opt/
sudo mv "/opt/sonar-scanner-${SONAR_VERSION}-linux" "$INSTALL_DIR"
# Set proper ownership and permissions
sudo chown -R root:root "$INSTALL_DIR"
sudo chmod -R 755 "$INSTALL_DIR"
sudo chmod 755 "$INSTALL_DIR/bin/sonar-scanner"
# Create symlink
sudo ln -sf "$INSTALL_DIR/bin/sonar-scanner" /usr/local/bin/sonar-scanner
# Cleanup
rm -f "sonar-scanner-cli-${SONAR_VERSION}-linux.zip"
}
# Configure SonarQube Scanner
configure_scanner() {
print_status "[4/7] Configuring SonarQube Scanner..."
local config_file="$INSTALL_DIR/conf/sonar-scanner.properties"
local sonar_host="${1:-https://sonarqube.example.com}"
local sonar_token="${2:-your-auth-token}"
sudo tee "$config_file" > /dev/null <<EOF
# SonarQube server configuration
sonar.host.url=$sonar_host
sonar.login=$sonar_token
# Global analysis properties
sonar.sourceEncoding=UTF-8
sonar.exclusions=**/test/**,**/*.spec.js,**/node_modules/**,**/vendor/**
sonar.coverage.exclusions=**/test/**,**/*.spec.js
# Language-specific settings
sonar.java.binaries=target/classes
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.python.coverage.reportPaths=coverage.xml
# Scanner settings
sonar.scanner.force-deprecated-java-version=false
EOF
sudo chown root:root "$config_file"
sudo chmod 644 "$config_file"
}
# Create sample project configuration
create_sample_config() {
print_status "[5/7] Creating sample project configuration..."
local sample_dir="/opt/sonar-scanner/samples"
sudo mkdir -p "$sample_dir"
# Create sonar-project.properties template
sudo tee "$sample_dir/sonar-project.properties.template" > /dev/null <<EOF
# Project identification
sonar.projectKey=my-project
sonar.projectName=My Project
sonar.projectVersion=1.0
# Source code location
sonar.sources=src
sonar.tests=test
# Language and encoding
sonar.sourceEncoding=UTF-8
# Exclusions
sonar.exclusions=**/*_test.go,**/*.spec.ts,**/node_modules/**
# Coverage reports (adjust paths as needed)
sonar.coverage.exclusions=**/*_test.go,**/*.spec.ts
sonar.go.coverage.reportPaths=coverage.out
sonar.javascript.lcov.reportPaths=coverage/lcov.info
EOF
sudo chown -R root:root "$sample_dir"
sudo chmod -R 644 "$sample_dir"/*
}
# Configure firewall (if needed)
configure_firewall() {
print_status "[6/7] Checking firewall configuration..."
# Note: SonarQube Scanner is a client tool, no inbound ports needed
print_status "SonarQube Scanner is a client tool - no firewall configuration required"
}
# Verify installation
verify_installation() {
print_status "[7/7] Verifying installation..."
# Check if sonar-scanner is executable
if ! command -v sonar-scanner >/dev/null 2>&1; then
print_error "SonarQube Scanner not found in PATH"
exit 1
fi
# Check version
local version_output
if version_output=$(sonar-scanner --version 2>&1); then
print_status "SonarQube Scanner installed successfully"
echo "$version_output" | head -3
else
print_error "SonarQube Scanner installation verification failed"
exit 1
fi
# Check Java dependency
if ! java -version >/dev/null 2>&1; then
print_warning "Java runtime not found. SonarQube Scanner requires Java 11 or later"
fi
}
# Main installation function
main() {
local sonar_host="${1:-}"
local sonar_token="${2:-}"
print_status "Starting SonarQube Scanner installation..."
check_privileges
detect_distro
install_dependencies
install_sonar_scanner
configure_scanner "$sonar_host" "$sonar_token"
create_sample_config
configure_firewall
verify_installation
print_status "Installation completed successfully!"
echo ""
echo -e "${GREEN}Next steps:${NC}"
echo "1. Update configuration in: $INSTALL_DIR/conf/sonar-scanner.properties"
echo "2. Copy sample config: cp $INSTALL_DIR/samples/sonar-project.properties.template ./sonar-project.properties"
echo "3. Configure your SonarQube server with custom quality gates and rules"
echo "4. Run analysis: sonar-scanner"
echo ""
echo -e "${YELLOW}Quality Gate Configuration:${NC}"
echo "- Log into SonarQube web interface as administrator"
echo "- Navigate to Quality Gates > Create"
echo "- Configure coverage, reliability, security, and maintainability conditions"
echo "- Set up custom rules for Java, JavaScript/TypeScript as needed"
}
# Parse arguments
if [[ $# -gt 2 ]]; then
usage
fi
main "$@"
Review the script before running. Execute with: bash install.sh