SonarQube is a code quality and security scanning tool that analyzes your codebase for bugs, vulnerabilities, code smells, and test coverage. Here's a practical walkthrough of getting started and the concepts you'll actually use day-to-day.
1. Creating an Account
The easiest way to start is via SonarCloud (SonarQube's hosted/cloud version):
- Go to sonarcloud.io
- Sign up using GitHub, GitLab, Bitbucket, or Azure DevOps (recommended — it links your repos directly)
- Create an Organization (usually tied to your GitHub username or org)
- Import the repository you want to analyze
Alternative: Self-host SonarQube (the on-prem Community Edition) via Docker if you don't want a cloud dependency:
docker run -d --name sonarqube -p 9000:9000 sonarqube:community
Then log in atlocalhost:9000(default:admin/admin).
2. Free Version Limitation — Public Repos Only
This is important to know upfront:
- SonarCloud's free tier only works with public repositories.
- If your repo is private, you'll need a paid plan on SonarCloud, or self-host the free SonarQube Community Edition instead (which has no public/private restriction, but you manage the server yourself).
So the decision is basically:
Need
Option
Public repo, no server management
SonarCloud (free)
Private repo, don't want to pay
Self-hosted SonarQube Community Edition
Private repo, want hosted convenience
SonarCloud paid plan
3. "Only Work on Previous Code" Option
SonarQube/SonarCloud has an analysis mode often labeled "New Code" vs "Overall Code."
- New Code — only evaluates code changed since a baseline (e.g. since last version, or last N days). This is the default and is meant to enforce quality going forward without forcing you to fix your entire legacy codebase at once.
- Overall Code (previous/existing code) — analyzes the entire codebase, including old/legacy code that existed before SonarQube was introduced.
If you want SonarQube to only evaluate old/existing code (i.e., not treat every new commit as fresh scope, or you're doing a one-time baseline audit), you set the New Code Definition to a fixed baseline, or simply run analysis in Overall Code mode from Project Settings → New Code.
This matters because most teams adopting SonarQube on a large legacy codebase don't want thousands of pre-existing issues blocking their first quality gate — they only enforce standards on new code, and separately track/clean up old code over time.
4. Configuration Files
sonar-project.properties
This is the main config file, placed in your project root. It tells the scanner what to analyze.
# Unique project identifier
sonar.projectKey=my-org_my-react-app
sonar.organization=my-org
# Project info
sonar.projectName=My React App
sonar.projectVersion=1.0
# Source code location
sonar.sources=src
sonar.tests=src
sonar.test.inclusions=**/*.test.ts,**/*.test.tsx
# Exclude files from analysis
sonar.exclusions=**/node_modules/**,**/build/**,**/*.config.js
# Coverage report path (e.g. from Jest/Vitest)
sonar.javascript.lcov.reportPaths=coverage/lcov.info
# Language-specific settings
sonar.sourceEncoding=UTF-8
Property
Purpose
sonar.projectKey
Unique ID for the project on the SonarQube server
sonar.organization
Required for SonarCloud (your org slug)
sonar.sources
Folder(s) to scan
sonar.exclusions
Folders/files to skip (node_modules, build output, etc.)
sonar.test.inclusions
Pattern to identify test files
sonar.javascript.lcov.reportPaths
Points to your coverage report so Sonar can read coverage %
Other config touchpoints
sonar-scannerCLI config (sonar-scanner.propertiesin the scanner's install directory) — global defaults if you're not using a per-project file.- Quality Profiles — configured on the SonarQube server UI, not in a file. These define which rules apply to which language.
- CI config file (e.g.
.github/workflows/sonar.yml) — defines when/how the scanner runs (covered in section 7).
5. Rules and Quality Gates
Rules
A Rule is a single check — e.g. "no unused variables," "cyclomatic complexity too high," "SQL injection risk." Rules are grouped into Quality Profiles (one profile per language, e.g. "Sonar way" is the default JS/TS profile).
Rules are categorized by type:
- Bug — code that's likely broken
- Vulnerability — security risk
- Code Smell — maintainability issue (not broken, but bad practice)
- Security Hotspot — needs manual review to confirm if it's actually a risk
Quality Gates
A Quality Gate is a set of pass/fail conditions your code must meet. If any condition fails, the build shows as "Failed" — commonly used to block merges in CI.
Default gate ("Sonar way") typically checks New Code for:
- Coverage ≥ 80%
- Duplicated lines < 3%
- Maintainability rating = A
- Reliability rating = A
- Security rating = A
- No new bugs/vulnerabilities left unresolved
You can customize thresholds under Project Settings → Quality Gates, or create a custom gate and assign it to specific projects.
6. Dashboard and Grades
After a scan, the project dashboard shows:
Metric
What it means
Bugs
Count of likely functional errors
Vulnerabilities
Security issues
Security Hotspots
Code needing manual security review
Code Smells
Maintainability issues
Coverage
% of code exercised by tests
Duplications
% of duplicated code blocks
Each of these rolls up into a letter grade (A–E):
- A = best (e.g. 0 issues, or issues with minimal impact)
- E = worst (critical issues present)
Ratings are calculated per category:
- Reliability rating — based on bugs
- Security rating — based on vulnerabilities
- Maintainability rating — based on code smells (technical debt ratio)
The dashboard also shows a big Quality Gate badge — green "Passed" or red "Failed" — at the top, which is usually the first thing your team checks after a PR scan.
7. Triggering Scans: Manual or via GitHub Actions
Manual trigger
Run the scanner locally whenever you want:
sonar-scanner \
-Dsonar.projectKey=my-org_my-react-app \
-Dsonar.organization=my-org \
-Dsonar.host.url=https://sonarcloud.io \
-Dsonar.login=$SONAR_TOKEN
Useful for one-off audits or before self-hosting CI integration.
GitHub Actions — trigger on specific branch commits
To automate scans only on pushes to a specific branch (e.g. main or develop):
# .github/workflows/sonar.ymlname: SonarQube Scan
on:
push:
branches:
- main # only trigger on commits to main
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # required for accurate blame/history data
- name: SonarCloud Scan
uses: SonarSource/sonarqube-scan-action@v4
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Notes:
SONAR_TOKENis generated from your SonarCloud/SonarQube account (My Account → Security → Generate Token) and stored as a GitHub repo secret.fetch-depth: 0ensures the full git history is available — Sonar uses it to correctly identify "new code" vs old code.- You can restrict further to pull requests targeting a branch using
on: pull_request: branches: [main]if you want gate checks before merge, instead of (or in addition to) push-based scanning.
Takeaway
To get going with SonarQube: sign up (free tier = public repos only, or self-host for private), drop a sonar-project.properties file in your repo root, understand that Quality Gates are what actually block/pass your build, keep an eye on the dashboard's letter grades, and wire up a GitHub Action scoped to your main branch so scans run automatically instead of manually every time.