Code Quality Gates with SonarQube: Setup & CI Integration 2026
SonarQube code quality gates guide: Docker setup, quality gate rules, GitHub Actions integration, code coverage thresholds, tracking technical debt, and team enforcement.
Code reviews catch logic errors. Linters catch style issues. But neither gives you a systematic, historical view of code quality trends — technical debt accumulation, security hotspots, declining test coverage. That’s the job of a quality gate.
SonarQube is the industry-standard platform for this. A quality gate is a set of conditions that must pass before code merges. If new code introduces a blocker bug, drops coverage below 80%, or adds critical security vulnerabilities, the build fails — automatically, before it reaches production.
This guide walks through setting up SonarQube from scratch, connecting it to a Node.js/TypeScript project, and enforcing quality gates in GitHub Actions.
What Is a Quality Gate?
A quality gate is a threshold-based pass/fail check on your code analysis. SonarQube’s default “Sonar Way” gate fails if new code has:
- Coverage on new code below 80%
- Any blocker or critical bugs
- Any security vulnerabilities
- Technical debt ratio above 5%
The critical phrase is “new code”. SonarQube tracks a “new code period” (typically since last version or last 30 days), so the gate applies pressure to new changes without requiring you to fix all legacy issues on day one.
Docker Compose Setup
The fastest way to run SonarQube locally or on a server is Docker.
# docker-compose.yml
version: '3.8'
services:
sonarqube:
image: sonarqube:10-community
container_name: sonarqube
ports:
- '9000:9000'
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
depends_on:
- db
db:
image: postgres:15
container_name: sonarqube_db
environment:
POSTGRES_USER: sonar
POSTGRES_PASSWORD: sonar
POSTGRES_DB: sonar
volumes:
- postgresql_data:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
postgresql_data:
System prerequisite — SonarQube requires vm.max_map_count >= 524288:
sudo sysctl -w vm.max_map_count=524288
# Make it permanent
echo "vm.max_map_count=524288" | sudo tee -a /etc/sysctl.conf
Start SonarQube:
docker compose up -d
Access http://localhost:9000. Default credentials: admin / admin. You’ll be prompted to change the password on first login.
Connecting a Node.js/TypeScript Project
Step 1: Generate a project token
In SonarQube UI: Administration → Security → Users → Tokens → Generate token. Copy the token — you’ll only see it once.
Step 2: Install sonar-scanner
npm install -D sonarqube-scanner
Step 3: Create sonar-project.properties
# sonar-project.properties
sonar.projectKey=my-nodejs-api
sonar.projectName=My Node.js API
sonar.projectVersion=1.0
# Source directory
sonar.sources=src
sonar.tests=src
sonar.test.inclusions=**/*.test.ts,**/*.spec.ts
sonar.exclusions=**/node_modules/**,**/dist/**,**/*.d.ts
# TypeScript configuration
sonar.typescript.lcov.reportPaths=coverage/lcov.info
# Encoding
sonar.sourceEncoding=UTF-8
Step 4: Add a scan script to package.json
{
"scripts": {
"test:coverage": "vitest run --coverage",
"sonar": "node -e \"const scanner = require('sonarqube-scanner'); scanner({ serverUrl: process.env.SONAR_HOST_URL, token: process.env.SONAR_TOKEN }, () => process.exit());\"",
"quality-check": "npm run test:coverage && npm run sonar"
}
}
Step 5: Run analysis locally
SONAR_HOST_URL=http://localhost:9000 \
SONAR_TOKEN=your_token_here \
npm run quality-check
After the scan completes, navigate to your project in the SonarQube UI to see the results.
GitHub Actions Integration
For PR-level analysis with inline annotations, use the sonarqube-scan-action:
# .github/workflows/quality-gate.yml
name: Code Quality Gate
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
sonarqube:
name: SonarQube Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for accurate blame information
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npm run 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 }}
Add these secrets in your GitHub repo: Settings → Secrets → Actions:
SONAR_TOKEN— the token generated earlierSONAR_HOST_URL— your SonarQube server URL (orhttps://sonarcloud.iofor SonarCloud)
When a PR is opened, SonarQube analyzes only the changed code and posts a PR decoration showing the quality gate status. If the gate fails, the GitHub check turns red.
Configuring Quality Gate Rules
In SonarQube UI: Quality Gates → Create → Add conditions.
A recommended starting point for a TypeScript backend:
Conditions on New Code:
- Coverage < 80% → FAILED
- Duplicated Lines (%) > 3% → FAILED
- Blocker Issues > 0 → FAILED
- Critical Issues > 0 → FAILED
- Security Rating worse than A → FAILED
- Reliability Rating worse than A → FAILED
- Maintainability Rating worse than B → WARN
Set your gate as the default: Quality Gates → your gate → Set as Default. Then assign it to your project: Project Settings → Quality Gate.
Reading SonarQube Reports
The project dashboard shows four key ratings, each graded A–E:
Reliability — Bug count and severity. An “A” rating means zero bugs. A single blocker bug drops you to “E”.
Security — Vulnerability count. SQL injection, hardcoded credentials, insecure random number generation are common findings.
Maintainability — Technical debt in hours/days. Based on code smells: overly complex functions, duplicated code, unused variables. The debt ratio compares estimated fix time to total development time.
Coverage — Line and branch coverage from your test reports.
Example findings for a Node.js API:
Reliability: A (0 bugs)
Security: B (2 vulnerabilities - SQL query without parameterization)
Maintainability: A (4h debt - 3 complex functions, 1 duplicated block)
Coverage: 82.4% (new code: 91.2%)
Click any finding to see the exact file, line number, and a remediation explanation.
SonarCloud for Open Source
If your project is open source, SonarCloud is free. It’s a hosted version of SonarQube with GitHub, GitLab, and Bitbucket integration built in. No Docker setup required.
Just connect your repository, and SonarCloud automatically detects your language and runs analysis on every push and PR.
For private projects, SonarCloud pricing is per-line-of-code. For small teams, self-hosted Community Edition (free) is often the better choice.
Fixing Common Issues
“Coverage report not found” — The lcov.info path in sonar-project.properties must match where your test runner outputs coverage. For Vitest: coverage/lcov.info. For Jest with --coverage: coverage/lcov.info.
“Quality Gate: FAILED” on first run — This is expected if your project has existing issues. Use the “Previous Version” new code period (Administration → Configuration → Settings → New Code) to focus the gate on code written after a specific baseline.
Duplicate code false positives — Add generated files to exclusions:
sonar.exclusions=**/node_modules/**,**/dist/**,**/__generated__/**,**/*.d.ts
Token authentication errors in CI — Verify the token has “Execute Analysis” permission and hasn’t expired. In newer SonarQube versions, use a project-scoped token rather than a user token for CI.
Technical Debt Tracking Over Time
One of SonarQube’s most underused features is trend analysis. The “Activity” tab on each project shows coverage, debt, and issue counts over time. Use this in sprint reviews: is technical debt growing or shrinking?
Set up a weekly Slack/Discord notification by using SonarQube’s webhook feature (Administration → Configuration → Webhooks) to POST to your notification service when an analysis completes.
Conclusion
Quality gates turn code quality from a vague aspiration into a concrete, automated enforcement mechanism. The investment is real: initial setup takes 2–4 hours, and fixing first-run findings takes longer. But once running, quality gates catch regressions automatically. Coverage stops quietly declining. Security issues get flagged before they reach production.
Start with SonarCloud if you want the fastest path to PR integration. Move to self-hosted Community Edition when you need more control or have private repositories at scale.
50 CLI Commands That Save Hours Every Week
The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.
- Git mastery (10 cmds)
- File & search wizardry
- Docker & DevOps tricks
- Process & network tools
- Productivity boosters
- macOS & Linux install guide
No spam. 1 email/week max. Unsubscribe anytime.
Found this guide useful? Check out our free developer tools.
Affiliate disclosure: Some links below are affiliate links — we may earn a small commission at no extra cost to you. Learn more.
Recommended Tools & Resources
DigitalOcean
$200 credit for new users. Simple, affordable cloud hosting for developers.
GitHub Student Pack
Free access to 100+ developer tools. Perfect for students and new devs.
Vercel
Deploy frontend apps instantly. Free tier is generous for side projects.
DevPlaybook Products
Boilerplates, scripts & AI toolkits to 10x your dev workflow.