Skip to content

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=262144 and restart the container.
  • Scanner: Not authorized: the token is empty or expired. Regenerate it and confirm echo $SONAR_TOKEN prints a value starting with squ_.
  • Scanner cannot reach the server from the container: --network host only works on Linux. On macOS/Windows use -e SONAR_HOST_URL=http://host.docker.internal:9000 instead.
  • Coverage shows 0% despite coverage.xml existing: path mismatch — the scanner runs in /usr/src so 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.sources points at the wrong folder. Confirm sonar-project.properties sits 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