beginner ~60 min updated 2026-06-01
SonarQube Code Scan
Run SonarQube Community Build in Docker, scan a real Python project with sonar-scanner CLI, read bugs, code smells, and coverage in the dashboard, and enforce a quality gate.
Objective
Stand up SonarQube locally, analyze a project with sonar-scanner including test coverage, and make the build fail on a quality gate breach. This mirrors how static analysis gates are wired into CI pipelines.
Prerequisites
- Docker installed with at least 4 GB of RAM available to containers
- Python 3.10 or newer
- sonar-scanner CLI installed, or willingness to run the scanner via its Docker image (used below)
- Basic familiarity with unit tests and coverage
Architecture
SonarQube server runs as a single Docker container with an embedded database (fine for labs). The scanner runs on your machine, parses the source code and the coverage report, and uploads the analysis to the server, which computes issues and evaluates the quality gate.
+--------------------+ analysis upload +-----------------------+
| sonar-scanner | ------------------> | SonarQube server |
| (Docker image) | token auth :9000 | - rules engine |
| reads: src/, | | - quality gate |
| coverage.xml | <------------------ | - web dashboard |
+--------------------+ gate pass/fail +-----------------------+
Steps
1. Start SonarQube
docker run -d --name sonarqube \
-p 9000:9000 \
-e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true \
sonarqube:community
# Wait until the API reports UP (1-2 minutes):
until curl -s http://localhost:9000/api/system/status | grep -q '"UP"'; do sleep 5; done
Open http://localhost:9000, log in with admin / admin, and set a new password when prompted.
2. Create a project and token
In the UI: Projects → Create project → Local project. Use the key python-lab. Then choose Locally and generate a token. Or do it via the API:
export SONAR_PASS='YourNewPassword1!'
curl -s -u admin:$SONAR_PASS -X POST \
"http://localhost:9000/api/projects/create?project=python-lab&name=python-lab"
export SONAR_TOKEN=$(curl -s -u admin:$SONAR_PASS -X POST \
"http://localhost:9000/api/user_tokens/generate?name=lab-token" | \
python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")
echo $SONAR_TOKEN
3. Create a Python project with deliberate issues
mkdir sonar-lab && cd sonar-lab
python3 -m venv .venv && source .venv/bin/activate
pip install pytest pytest-cov
mkdir src tests
# src/calc.py
def divide(a, b):
# BUG: no zero check; also a code smell below
result = a / b
unused = "this variable is never used"
if result == True: # smell: comparison to True
print("one")
return result
def add(a, b):
return a + b
# tests/test_calc.py
from src.calc import add
def test_add():
assert add(2, 3) == 5
4. Run tests with coverage
python -m pytest --cov=src --cov-report=xml:coverage.xml tests/
5. Configure and run the scanner
# sonar-project.properties
sonar.projectKey=python-lab
sonar.sources=src
sonar.tests=tests
sonar.python.coverage.reportPaths=coverage.xml
sonar.python.version=3.12
docker run --rm \
--network host \
-e SONAR_HOST_URL=http://localhost:9000 \
-e SONAR_TOKEN=$SONAR_TOKEN \
-v "$PWD:/usr/src" \
sonarsource/sonar-scanner-cli
6. Review results and enforce the gate
Open http://localhost:9000/dashboard?id=python-lab and review the issues (you should see the unused variable and the == True comparison flagged, and low coverage on divide). To make CI fail on a gate breach, re-run the scanner with:
docker run --rm --network host \
-e SONAR_HOST_URL=http://localhost:9000 \
-e SONAR_TOKEN=$SONAR_TOKEN \
-v "$PWD:/usr/src" \
sonarsource/sonar-scanner-cli \
-Dsonar.qualitygate.wait=true
echo "Exit code: $?"
Expected output
$ python -m pytest --cov=src --cov-report=xml:coverage.xml tests/
tests/test_calc.py . [100%]
---------- coverage: platform linux, python 3.12 -----------
src/calc.py 9 5 44%
1 passed in 0.05s
Scanner log:
INFO ANALYSIS SUCCESSFUL, you can find the results at:
INFO http://localhost:9000/dashboard?id=python-lab
With -Dsonar.qualitygate.wait=true on a failing gate:
ERROR QUALITY GATE STATUS: FAILED - View details on ...
Exit code: 2
Troubleshooting
- SonarQube container exits with
max virtual memory areas vm.max_map_count [65530] is too low: raise it on the Docker host:sudo sysctl -w vm.max_map_count=262144and restart the container. - Scanner:
Not authorized: the token is empty or expired. Regenerate it and confirmecho $SONAR_TOKENprints a value starting withsqu_. - Scanner cannot reach the server from the container:
--network hostonly works on Linux. On macOS/Windows use-e SONAR_HOST_URL=http://host.docker.internal:9000instead. - Coverage shows 0% despite coverage.xml existing: path mismatch — the scanner runs in
/usr/srcso the report must be referenced relative to project root and generated with relative paths (run pytest from the project root, as shown). - Dashboard shows “no lines of code”:
sonar.sourcespoints at the wrong folder. Confirmsonar-project.propertiessits in the directory you mount.
Cleanup
deactivate
docker rm -f sonarqube
docker rmi sonarqube:community sonarsource/sonar-scanner-cli 2>/dev/null || true
cd .. && rm -rf sonar-lab