SonarQube GitLab CI Integration
GitLab runs your build in a pipeline defined by a single file. Adding a SonarQube job to that file analyzes every push automatically. Developers see results without leaving their normal workflow, and weak code stops before it merges.
The Conveyor Belt Picture
git push
│
▼
┌────────┐ ┌────────┐ ┌─────────────┐ ┌────────┐
│ build │──►│ test │──►│ sonar scan │──►│ deploy │
└────────┘ └────────┘ └──────┬──────┘ └────────┘
│ report
▼
SonarQube Server
(pass or fail)
The scan job sits between testing and delivery like a checkpoint guard.
Preparation
- Create the project in SonarQube and note its project key.
- Generate an analysis token in SonarQube.
- Open the GitLab project and go to Settings, then CI/CD, then Variables.
- Add a variable named SONAR_TOKEN with the token value. Mark it as masked.
- Add a variable named SONAR_HOST_URL with the server address.
Variables keep secrets out of the repository.
The Pipeline File
Add this job to .gitlab-ci.yml.
stages:
- test
- quality
sonarqube-check:
stage: quality
image:
name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""]
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: "0"
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- sonar-scanner -Dsonar.projectKey=shop-backend -Dsonar.qualitygate.wait=true
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Line by Line
Image
The official scanner image brings the tool without any installation. The empty entrypoint lets GitLab run its own commands inside the container.
GIT_DEPTH
A value of 0 downloads the full history. SonarQube needs history to assign new code correctly and to blame the right authors.
Cache
Scanner downloads land in a cache folder, so later runs start faster.
Quality Gate Wait
The wait flag turns a failed gate into a failed job. A red job blocks the merge when you require passing pipelines.
Rules
The job runs on merge requests and on the default branch. Pipelines for other branches skip it and save minutes.
Passing Test Coverage
Run tests in an earlier job and share the report as an artifact. The scan job reads the file through a property.
test:
stage: test
script:
- pytest --cov --cov-report=xml
artifacts:
paths:
- coverage.xml
Add -Dsonar.python.coverage.reportPaths=coverage.xml to the scanner command for Python projects. Other languages use their own property names.
Maven Projects
Java teams can skip the scanner image and call Maven directly.
script: - mvn verify sonar:sonar -Dsonar.projectKey=shop-backend
Use a Maven image for the job and keep the same variables.
Merge Request Feedback
Branch and merge request analysis belongs to the Developer Edition and above. These editions comment results directly in the merge request after you configure the GitLab connection in the project settings. The Community Build analyzes the main branch only.
Troubleshooting
| Symptom | Likely Cause |
|---|---|
| Not authorized | Wrong token or unmasked typo in the variable |
| Cannot reach server | The runner cannot access SONAR_HOST_URL |
| Wrong blame data | Shallow clone, so set GIT_DEPTH to 0 |
| Pipeline passes but gate fails | Missing wait flag |
Good Habits
- Store tokens in masked variables only.
- Use protected variables for production branches.
- Keep the scan job fast by caching and excluding generated files.
- Check the Background Tasks page when a job waits too long.
A Walkthrough
A developer opens a merge request. GitLab starts the pipeline, and the build and test jobs finish in four minutes. The sonarqube-check job downloads the cached scanner, analyzes the code, and uploads the report. SonarQube processes the report and answers pass or fail. The job turns green or red accordingly, and the reviewer sees the result on the merge request page before reading a single line of code.
Self-Managed and Cloud Runners
Runners that live inside your company network reach a private SonarQube server easily. Cloud-hosted runners need a public address or a secure tunnel. Confirm the connection with a simple test job that calls the status endpoint before you add the full scan.
Quick Recap
Add two variables, add one job, fetch full history, and let the quality gate decide. Every merge request now meets the same quality bar.
