SonarQube Quality Gates: Enforce Code Standards on Every PR
A Quality Gate is a pass/fail threshold in SonarQube — it answers the question "is this code good enough to merge?" You configure conditions (e.g., "no new critical bugs", "coverage on new code ≥ 80%") and the gate either passes or fails the analysis. Integrate the gate status into your CI/CD pipeline and you can block merges automatically when code quality drops.
What Is a Quality Gate?
A Quality Gate is a set of conditions applied to your SonarQube analysis results. After each scan, SonarQube evaluates every condition and marks the gate as Passed or Failed.
Common conditions:
- Coverage on new code ≥ 80%
- Duplicated lines on new code < 3%
- Maintainability Rating on new code = A
- Reliability Rating on new code = A
- Security Rating on new code = A
- New critical/blocker issues = 0
The key phrase is "on new code" — by default, SonarQube's quality gates apply to code introduced since the previous clean analysis (the "new code period"), not the entire codebase. This makes quality gates actionable for existing projects that have accumulated technical debt.
The Default "Sonar Way" Gate
SonarQube ships with a built-in gate called Sonar Way. As of SonarQube 10, Sonar Way conditions are:
| Condition | Threshold |
|---|---|
| Coverage on new code | ≥ 80% |
| Duplicated lines on new code | < 3% |
| Maintainability Rating on new code | A |
| Reliability Rating on new code | A |
| Security Rating on new code | A |
| Security Hotspots Reviewed on new code | ≥ 100% |
You can use Sonar Way as-is or create a custom gate based on your team's standards.
Creating a Custom Quality Gate
Go to Quality Gates in the SonarQube UI:
- Click Create and give it a name (e.g., "Team Standard")
- Add conditions:
- Choose On New Code or Overall Code
- Pick the metric (e.g.,
Coverage,Blocker Issues,Critical Issues) - Set the operator and threshold
- Set it as the default gate for new projects
For a team starting out, a stricter-than-Sonar-Way gate might look like:
| Condition | Threshold |
|---|---|
| Coverage on new code | ≥ 85% |
| New blocker issues | = 0 |
| New critical issues | = 0 |
| Security Rating on new code | A |
Assigning Quality Gates to Projects
Each project can use a different gate:
- Go to your project → Project Settings → Quality Gate
- Select Always use a specific Quality Gate
- Choose your gate
Or set the default gate that all new projects inherit under Administration → Quality Gates → select gate → Set as Default.
Integrating with CI/CD
The most important integration: make your pipeline fail when the quality gate fails.
GitHub Actions
# .github/workflows/sonarqube.yml
name: SonarQube Analysis
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for new code detection
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies and run tests
run: |
npm ci
npm test -- --coverage
- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
- name: Check Quality Gate
uses: sonarsource/sonarqube-quality-gate-action@master
timeout-minutes: 5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}The sonarqube-quality-gate-action step polls the SonarQube server until the analysis completes, then exits with a non-zero code if the gate fails — causing the pipeline to fail.
GitLab CI
sonarqube-analysis:
image: sonarsource/sonar-scanner-cli:latest
stage: test
variables:
SONAR_HOST_URL: $SONAR_HOST_URL
SONAR_TOKEN: $SONAR_TOKEN
script:
- sonar-scanner
-Dsonar.projectKey=$CI_PROJECT_NAME
-Dsonar.sources=.
-Dsonar.qualitygate.wait=true # Block until gate is evaluated
allow_failure: false # Fail the pipeline on gate failureThe sonar.qualitygate.wait=true property makes the scanner block until the quality gate is evaluated, then return a non-zero exit code if it fails.
Jenkins
pipeline {
agent any
stages {
stage('Test') {
steps {
sh 'npm test -- --coverage'
}
}
stage('SonarQube Analysis') {
environment {
SCANNER_HOME = tool 'SonarScanner'
}
steps {
withSonarQubeEnv('SonarQube') {
sh "${SCANNER_HOME}/bin/sonar-scanner"
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
}
}The waitForQualityGate abortPipeline: true step waits for SonarQube's webhook callback and aborts the pipeline if the gate fails.
Setting Up the Webhook (Required for Jenkins/Self-Hosted)
For the quality gate check to work synchronously in CI, SonarQube must call back to your CI server when analysis finishes.
In SonarQube: Administration → Configuration → Webhooks → Create
Name: Jenkins
URL: http://your-jenkins-host/sonarqube-webhook/
Secret: (leave blank or set a secret for verification)For GitHub Actions, the sonarqube-quality-gate-action handles this via polling — no webhook needed.
Understanding Quality Gate Conditions
Metric Categories
Reliability — based on bug count:
- Rating A = 0 bugs
- Rating B = at least 1 minor bug
- Rating C = at least 1 major bug
- Rating D = at least 1 critical bug
- Rating E = at least 1 blocker bug
Security — based on vulnerability count (same A-E scale)
Maintainability — based on technical debt ratio (debt time / development time):
- A = ≤ 5%
- B = 6–10%
- C = 11–20%
- D = 21–50%
- E = > 50%
Coverage — line and branch coverage percentage from your test reports
Duplications — percentage of duplicated code blocks
New Code vs Overall Code
Quality gates have two scopes:
- On New Code: Conditions apply only to code changed since the reference branch or period. Best for incremental improvement — you can't fail for old debt.
- Overall Code: Conditions apply to the entire codebase. Useful for greenfield projects or when you've cleaned up legacy debt.
Most teams use On New Code conditions. This lets you have a low-quality legacy codebase while enforcing strict standards on all new contributions.
Practical Configuration Examples
Startup Team (Getting Started)
Be strict on security, permissive on coverage:
New blocker issues = 0
New critical issues = 0
Security Rating on new code = A
Security Hotspots Reviewed on new code ≥ 100%
Coverage on new code ≥ 60%Established Engineering Team
Higher coverage bar, no regressions:
Coverage on new code ≥ 80%
Maintainability Rating on new code = A
Reliability Rating on new code = A
Security Rating on new code = A
New blocker issues = 0
Duplicated lines on new code < 3%Financial/Healthcare (High Compliance)
Every issue must be reviewed:
Coverage on new code ≥ 90%
New issues = 0 (any severity)
Security Rating on new code = A
Security Hotspots Reviewed = 100%Reading Quality Gate Failures
When a gate fails, the SonarQube dashboard shows which conditions failed:
Quality Gate FAILED
✗ Coverage on New Code: 62.3% (required ≥ 80%)
✗ New Blocker Issues: 1 (required = 0)
✓ Reliability Rating on New Code: A
✓ Security Rating on New Code: AClick the failed condition to see exactly which files and lines are responsible. For coverage failures, SonarQube shows the uncovered new lines in red. For issue failures, you see the specific rule violations.
Common Mistakes
Setting thresholds too high too early — a 90% coverage gate on a codebase that currently has 40% coverage will block every PR. Start at your current level + 5%, raise it quarterly.
Ignoring Security Hotspots — hotspots aren't vulnerabilities (they need human review), but they still need to be marked "Reviewed". Teams often miss this condition and are surprised when the gate fails despite no "real" vulnerabilities.
Not setting fetch-depth: 0 — in GitHub Actions, the default shallow clone means SonarQube can't determine what's new code. Always use fetch-depth: 0.
Gate not blocking merges — configuring the gate in SonarQube isn't enough. You also need branch protection rules in GitHub/GitLab that require the SonarQube check to pass before merging.
Enforcing Gates in GitHub
Add a required status check in GitHub:
- Go to repository → Settings → Branches → Add rule
- Enable Require status checks to pass before merging
- Search for and add
SonarQube(or whatever the check is named in your Actions workflow)
Now PRs that fail the quality gate can't be merged without a manual override — exactly the enforcement quality gates are designed for.