Docker Compose Reference: SonarQube

Type: Software Reference Confidence: 0.93 Sources: 7 Verified: 2026-02-27 Freshness: 2026-02-27

TL;DR

Constraints

Quick Reference

Service Configuration

ServiceImagePortsVolumesKey Env
sonarqubesonarqube:community9000:9000sonarqube_data:/opt/sonarqube/dataSONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar
sonarqubesonarqube:developer9000:9000sonarqube_extensions:/opt/sonarqube/extensionsSONAR_JDBC_USERNAME=sonar
sonarqubesonarqube:enterprise9000:9000sonarqube_logs:/opt/sonarqube/logsSONAR_JDBC_PASSWORD=sonar
postgresqlpostgres:175432 (internal)postgresql_data:/var/lib/postgresql/dataPOSTGRES_USER=sonar
postgresqlpostgres:165432 (internal)(same)POSTGRES_PASSWORD=sonar
postgresqlpostgres:155432 (internal)(same)POSTGRES_DB=sonar
sonar-scannersonarsource/sonar-scanner-cli:11none/usr/src (mount code)SONAR_HOST_URL, SONAR_TOKEN
sonarqube (tmpfs)(any edition)--sonarqube_temp:/opt/sonarqube/temptmpfs: /tmp (256M)

Edition Comparison

FeatureCommunity (Free)DeveloperEnterpriseData Center
Languages17+ (Java, JS, Python, C#, etc.)30+ (incl. C, C++, Obj-C, Swift)30+30+
Branch analysisNoYesYesYes
PR decorationNoYes (GitHub, GitLab, Azure, Bitbucket)YesYes
Portfolio managementNoNoYesYes
High availabilityNoNoNoYes
Docker image tagsonarqube:communitysonarqube:developersonarqube:enterprisesonarqube:datacenter-app
PriceFree (LGPL v3)CommercialCommercialCommercial

Decision Tree

START: What SonarQube Docker setup do you need?
├── Need quick local evaluation?
│   ├── YES → Use sonarqube:community with embedded H2
│   └── NO (production) ↓
├── Single instance or high availability?
│   ├── Single instance ↓
│   │   ├── Need branch analysis / PR decoration?
│   │   │   ├── YES → Use sonarqube:developer + PostgreSQL
│   │   │   └── NO → Use sonarqube:community + PostgreSQL
│   │   └── Resource sizing:
│   │       ├── < 50 projects → 4 GB RAM, 2 CPU cores
│   │       ├── 50-200 projects → 8 GB RAM, 4 CPU cores
│   │       └── > 200 projects → Consider Enterprise edition
│   └── High availability → Data Center edition
└── CI/CD integration?
    ├── GitHub Actions → SonarSource/sonarqube-scan-action
    ├── GitLab CI → sonarsource/sonar-scanner-cli:11 image
    ├── Jenkins → SonarQube Scanner for Jenkins plugin
    └── CLI/Manual → sonar-scanner CLI or Docker image

Step-by-Step Guide

1. Configure host kernel parameters

SonarQube embeds Elasticsearch, which requires specific Linux kernel settings on the Docker host. [src5]

# Set temporarily (resets on reboot)
sudo sysctl -w vm.max_map_count=524288
sudo sysctl -w fs.file-max=131072

# Set permanently (survives reboot)
echo "vm.max_map_count=524288" | sudo tee -a /etc/sysctl.d/99-sonarqube.conf
echo "fs.file-max=131072" | sudo tee -a /etc/sysctl.d/99-sonarqube.conf
sudo sysctl --system

Verify: sysctl vm.max_map_count → expected: vm.max_map_count = 524288

2. Create docker-compose.yml

Create the compose file with SonarQube and PostgreSQL. Use named volumes, not bind mounts. [src1] [src2]

services:
  sonarqube:
    image: sonarqube:community
    read_only: true
    depends_on:
      db:
        condition: service_healthy
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: ${SONAR_DB_PASSWORD:-sonar}
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
      - sonarqube_temp:/opt/sonarqube/temp
    tmpfs:
      - /tmp:size=256m
    ports:
      - "9000:9000"
  db:
    image: postgres:17
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: ${SONAR_DB_PASSWORD:-sonar}
      POSTGRES_DB: sonar
    volumes:
      - postgresql_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U sonar -d sonar"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  sonarqube_temp:
  postgresql_data:

Verify: docker compose config → no errors in output

3. Start the services

Launch SonarQube and wait for it to become healthy. First startup takes 1-3 minutes. [src1]

docker compose up -d
docker compose logs -f sonarqube
# Wait for "SonarQube is operational" message

Verify: curl -s http://localhost:9000/api/system/status | jq .status → expected: "UP"

4. Change default admin password

Change the default admin/admin credentials immediately. [src1]

curl -u admin:admin -X POST \
  "http://localhost:9000/api/users/change_password" \
  -d "login=admin&previousPassword=admin&password=YOUR_SECURE_PASSWORD"

Verify: curl -u admin:YOUR_SECURE_PASSWORD http://localhost:9000/api/system/status → returns 200 OK

5. Generate a scanner token

Create an authentication token for the scanner to use in CI/CD. [src4]

curl -u admin:YOUR_SECURE_PASSWORD -X POST \
  "http://localhost:9000/api/user_tokens/generate" \
  -d "name=ci-scanner&type=GLOBAL_ANALYSIS_TOKEN"

Verify: Navigate to http://localhost:9000/account/security → token appears in list

6. Configure sonar-project.properties

Place this file in your project root. [src1]

sonar.projectKey=my-project
sonar.projectName=My Project
sonar.sources=src
sonar.tests=test
sonar.sourceEncoding=UTF-8
sonar.qualitygate.wait=true
sonar.qualitygate.timeout=300

7. Run SonarScanner

Run the scanner against your project using Docker. [src6]

docker run --rm \
  --network=sonarnet \
  -e SONAR_HOST_URL="http://sonarqube:9000" \
  -e SONAR_TOKEN="your_generated_token" \
  -v "$(pwd):/usr/src" \
  sonarsource/sonar-scanner-cli:11

Verify: Check http://localhost:9000/dashboard?id=my-project → analysis results appear

Code Examples

GitHub Actions: SonarQube Analysis

# .github/workflows/sonarqube.yml
name: SonarQube Analysis
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  sonarqube:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: SonarQube Scan
        uses: SonarSource/sonarqube-scan-action@v4
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
      - name: Quality Gate
        uses: SonarSource/sonarqube-quality-gate-action@v1
        timeout-minutes: 5
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

GitLab CI: SonarQube Analysis

# .gitlab-ci.yml
sonarqube-check:
  image:
    name: sonarsource/sonar-scanner-cli:11
    entrypoint: [""]
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
    GIT_DEPTH: "0"
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
  script:
    - sonar-scanner
      -Dsonar.host.url=${SONAR_HOST_URL}
      -Dsonar.token=${SONAR_TOKEN}
      -Dsonar.projectKey=${CI_PROJECT_PATH_SLUG}
      -Dsonar.qualitygate.wait=true

Docker Compose: Production Hardened Setup

Full script: docker-compose-production.yml (62 lines)

# Production-hardened docker-compose.yml with resource limits,
# health checks, restart policies, and reverse proxy readiness.
services:
  sonarqube:
    image: sonarqube:developer
    read_only: true
    deploy:
      resources:
        limits: { memory: 4g, cpus: "2.0" }
        reservations: { memory: 2g }
    # ... (see full script)

Bash: Backup SonarQube Data

#!/bin/bash
BACKUP_DIR="./backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
docker compose exec -T db pg_dump -U sonar sonar \
  | gzip > "$BACKUP_DIR/sonar_db.sql.gz"
docker run --rm \
  -v sonarqube_extensions:/data \
  -v "$(pwd)/$BACKUP_DIR":/backup \
  alpine tar czf /backup/extensions.tar.gz -C /data .
echo "Backup saved to $BACKUP_DIR"

Anti-Patterns

Wrong: Using bind mounts for SonarQube volumes

# BAD -- bind mounts prevent plugins from populating correctly
services:
  sonarqube:
    image: sonarqube:community
    volumes:
      - ./sonarqube/data:/opt/sonarqube/data
      - ./sonarqube/extensions:/opt/sonarqube/extensions

Correct: Using named Docker volumes

# GOOD -- named volumes let SonarQube populate directories correctly
services:
  sonarqube:
    image: sonarqube:community
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
volumes:
  sonarqube_data:
  sonarqube_extensions:

Wrong: Skipping PostgreSQL health check

# BAD -- SonarQube starts before DB is ready, causes JDBC errors
services:
  sonarqube:
    depends_on:
      - db  # only waits for container start, not readiness

Correct: Using health check with condition

# GOOD -- waits until PostgreSQL is actually ready
services:
  sonarqube:
    depends_on:
      db:
        condition: service_healthy
  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U sonar -d sonar"]
      interval: 10s
      timeout: 5s
      retries: 5

Wrong: Using H2 database in production

# BAD -- H2 is for evaluation only, no upgrade path
services:
  sonarqube:
    image: sonarqube:community
    # No SONAR_JDBC_URL = uses embedded H2

Correct: Always use PostgreSQL for production

# GOOD -- PostgreSQL provides durability and upgradability
services:
  sonarqube:
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: ${SONAR_DB_PASSWORD}

Wrong: Hardcoding secrets in docker-compose.yml

# BAD -- secrets committed to version control
services:
  sonarqube:
    environment:
      SONAR_JDBC_PASSWORD: my_secret_password_123

Correct: Using environment variables or Docker secrets

# GOOD -- use .env file (not committed to git)
services:
  sonarqube:
    environment:
      SONAR_JDBC_PASSWORD: ${SONAR_DB_PASSWORD}

Common Pitfalls

Diagnostic Commands

# Check SonarQube service status
curl -s http://localhost:9000/api/system/status | jq .

# View SonarQube logs
docker compose logs sonarqube --tail=100

# Check PostgreSQL readiness
docker compose exec db pg_isready -U sonar -d sonar

# Check host kernel parameters
sysctl vm.max_map_count fs.file-max

# Verify volumes exist
docker volume ls | grep sonar

# Check resource usage
docker compose stats --no-stream

# List installed plugins
curl -s http://localhost:9000/api/plugins/installed -u admin:password | jq '.plugins[].key'

# Check quality gate status for a project
curl -s "http://localhost:9000/api/qualitygates/project_status?projectKey=my-project" \
  -u admin:password | jq .projectStatus.status

Version History & Compatibility

VersionStatusBreaking ChangesMigration Notes
2025.1 LTACurrent LTAYear-based versioning; read_only filesystem in composeUpgrade from 9.9 LTS requires intermediate 10.x step
10.8Current (non-LTA)read_only filesystem in official composeUpdate compose file to include tmpfs
10.7MaintainedNoneDirect upgrade from 10.6
9.9 LTSEnd of life (2025)--Must upgrade to 10.x first
8.9 LTSEnd of lifeDropped Oracle 11g, IE11Must upgrade to 9.9 LTS first

PostgreSQL Compatibility

PostgreSQLSonarQube 10.xSonarQube 2025.1 LTA
PostgreSQL 17SupportedSupported
PostgreSQL 16SupportedSupported
PostgreSQL 15SupportedSupported
PostgreSQL 14SupportedSupported
PostgreSQL 13SupportedSupported (minimum)
PostgreSQL 12DeprecatedNot supported

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Self-hosting SonarQube for code quality analysisTeam prefers managed SaaSSonarQube Cloud (sonarcloud.io)
Need full control over data and configurationQuick one-time code scan without infrastructureSonarLint IDE plugin
CI/CD pipeline needs automated quality gatesOnly need linting without security analysisESLint, Pylint, or language-specific linters
Organization requires air-gapped/on-premise deploymentBudget constraints and < 5 developersSonarQube Cloud free tier
Need branch analysis and PR decorationOnly scanning a single branchCommunity edition may suffice
High availability requiredSingle team, < 100 projectsDeveloper or Enterprise edition

Important Caveats

Related Units