docker compose up -d with a compose file defining sonarqube:community (or developer/enterprise) and postgres:17 services.vm.max_map_count on the Docker host causes Elasticsearch to crash on startup.vm.max_map_count >= 524288 and fs.file-max >= 131072 -- SonarQube's embedded Elasticsearch will fail to start otherwise| Service | Image | Ports | Volumes | Key Env |
|---|---|---|---|---|
| sonarqube | sonarqube:community | 9000:9000 | sonarqube_data:/opt/sonarqube/data | SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar |
| sonarqube | sonarqube:developer | 9000:9000 | sonarqube_extensions:/opt/sonarqube/extensions | SONAR_JDBC_USERNAME=sonar |
| sonarqube | sonarqube:enterprise | 9000:9000 | sonarqube_logs:/opt/sonarqube/logs | SONAR_JDBC_PASSWORD=sonar |
| postgresql | postgres:17 | 5432 (internal) | postgresql_data:/var/lib/postgresql/data | POSTGRES_USER=sonar |
| postgresql | postgres:16 | 5432 (internal) | (same) | POSTGRES_PASSWORD=sonar |
| postgresql | postgres:15 | 5432 (internal) | (same) | POSTGRES_DB=sonar |
| sonar-scanner | sonarsource/sonar-scanner-cli:11 | none | /usr/src (mount code) | SONAR_HOST_URL, SONAR_TOKEN |
| sonarqube (tmpfs) | (any edition) | -- | sonarqube_temp:/opt/sonarqube/temp | tmpfs: /tmp (256M) |
| Feature | Community (Free) | Developer | Enterprise | Data Center |
|---|---|---|---|---|
| Languages | 17+ (Java, JS, Python, C#, etc.) | 30+ (incl. C, C++, Obj-C, Swift) | 30+ | 30+ |
| Branch analysis | No | Yes | Yes | Yes |
| PR decoration | No | Yes (GitHub, GitLab, Azure, Bitbucket) | Yes | Yes |
| Portfolio management | No | No | Yes | Yes |
| High availability | No | No | No | Yes |
| Docker image tag | sonarqube:community | sonarqube:developer | sonarqube:enterprise | sonarqube:datacenter-app |
| Price | Free (LGPL v3) | Commercial | Commercial | Commercial |
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
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
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
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"
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
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
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
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
# .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.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
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)
#!/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"
# BAD -- bind mounts prevent plugins from populating correctly
services:
sonarqube:
image: sonarqube:community
volumes:
- ./sonarqube/data:/opt/sonarqube/data
- ./sonarqube/extensions:/opt/sonarqube/extensions
# 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:
# BAD -- SonarQube starts before DB is ready, causes JDBC errors
services:
sonarqube:
depends_on:
- db # only waits for container start, not readiness
# 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
# BAD -- H2 is for evaluation only, no upgrade path
services:
sonarqube:
image: sonarqube:community
# No SONAR_JDBC_URL = uses embedded H2
# 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}
# BAD -- secrets committed to version control
services:
sonarqube:
environment:
SONAR_JDBC_PASSWORD: my_secret_password_123
# GOOD -- use .env file (not committed to git)
services:
sonarqube:
environment:
SONAR_JDBC_PASSWORD: ${SONAR_DB_PASSWORD}
max virtual memory areas vm.max_map_count [65530] is too low. Fix: sudo sysctl -w vm.max_map_count=524288 on the Docker host. [src5]vm.max_map_count, fs.file-max, and ulimits on the host system. [src5]depends_on: { condition: service_healthy }. [src2]sonar.qualitygate.wait=true hangs. Fix: Add sonar.qualitygate.timeout=300. [src4]-v flag removes named volumes. Fix: Use docker compose down without -v; create regular backups. [src1]SONAR_WEB_JAVAOPTS=-Xmx2g and SONAR_CE_JAVAOPTS=-Xmx2g. [src3]# 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 | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| 2025.1 LTA | Current LTA | Year-based versioning; read_only filesystem in compose | Upgrade from 9.9 LTS requires intermediate 10.x step |
| 10.8 | Current (non-LTA) | read_only filesystem in official compose | Update compose file to include tmpfs |
| 10.7 | Maintained | None | Direct upgrade from 10.6 |
| 9.9 LTS | End of life (2025) | -- | Must upgrade to 10.x first |
| 8.9 LTS | End of life | Dropped Oracle 11g, IE11 | Must upgrade to 9.9 LTS first |
| PostgreSQL | SonarQube 10.x | SonarQube 2025.1 LTA |
|---|---|---|
| PostgreSQL 17 | Supported | Supported |
| PostgreSQL 16 | Supported | Supported |
| PostgreSQL 15 | Supported | Supported |
| PostgreSQL 14 | Supported | Supported |
| PostgreSQL 13 | Supported | Supported (minimum) |
| PostgreSQL 12 | Deprecated | Not supported |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Self-hosting SonarQube for code quality analysis | Team prefers managed SaaS | SonarQube Cloud (sonarcloud.io) |
| Need full control over data and configuration | Quick one-time code scan without infrastructure | SonarLint IDE plugin |
| CI/CD pipeline needs automated quality gates | Only need linting without security analysis | ESLint, Pylint, or language-specific linters |
| Organization requires air-gapped/on-premise deployment | Budget constraints and < 5 developers | SonarQube Cloud free tier |
| Need branch analysis and PR decoration | Only scanning a single branch | Community edition may suffice |
| High availability required | Single team, < 100 projects | Developer or Enterprise edition |