ITSkillsCenter
Business Digital

Auto-héberger SonarQube Community Build 26.4 sur un VPS Linux pas-à-pas

13 min de lecture

📍 Guide principal du sujet : Pipeline SAST DAST SCA 2026 : architecture, outils et intégration CI/CD
Ce tutoriel détaille l’installation et la configuration de SonarQube Community Build comme brique SAST. Pour la vue d’ensemble du pipeline DevSecOps, lire d’abord le guide principal.

Pourquoi auto-héberger SonarQube Community Build en 2026

SonarQube reste en 2026 l’un des outils SAST les plus utilisés en entreprise, avec une part dominante sur le segment statique pour les équipes éditoriales internes. La version Community Build, distribuée gratuitement sous licence LGPL v3, couvre 30+ langages, intègre une base de règles de sécurité dérivée d’OWASP Top 10 et de CWE, et propose une interface mature pour suivre l’évolution de la dette technique. Le calendrier de release a basculé en 2024 sur un rythme mensuel : v26.4.0 a été publiée en avril 2026, avec un schéma de version YY.M.0 qui clarifie l’ancienneté de chaque déploiement.

Auto-héberger plutôt que souscrire à SonarCloud présente trois avantages décisifs : le code source ne quitte jamais l’infrastructure, le coût est constant quel que soit le nombre de commiters, et l’instance peut être branchée sur des dépôts internes inaccessibles depuis Internet. La contrepartie : il faut administrer le service, gérer les mises à jour mensuelles, sauvegarder la base PostgreSQL et dimensionner correctement la JVM. Ce tutoriel déroule l’installation pas-à-pas avec Docker Compose et un reverse proxy Nginx en TLS, configuration adaptée à une équipe de 5 à 50 développeurs.

Prérequis

  • Un VPS ou serveur dédié sous Ubuntu 24.04 LTS, Debian 12 ou Rocky Linux 9 (4 vCPU, 8 Go de RAM, 50 Go SSD minimum).
  • Docker Engine 20.10+ et Docker Compose v2 installés et fonctionnels.
  • Un nom de domaine pointant sur l’IP publique du serveur (pour le TLS).
  • Java 21 (OpenJDK ou Temurin) sur la machine où le scanner sera exécuté — la JVM embarquée dans le serveur Sonar est gérée par l’image Docker, mais le sonar-scanner côté CI a besoin de sa propre JRE.
  • Niveau attendu : intermédiaire Linux, à l’aise avec Docker Compose et l’édition de fichiers de configuration.
  • Temps estimé : 90 minutes pour l’installation complète et le premier scan validé.

Étape 1 — Préparer les paramètres noyau exigés par Elasticsearch embarqué

SonarQube embarque une instance Elasticsearch pour indexer les findings et accélérer les recherches. Elasticsearch impose au noyau Linux des limites précises sur le nombre de zones mémoire qu’un processus peut allouer ; sans ces réglages, le serveur démarre puis s’arrête en boucle avec une erreur bootstrap check failed. Cette première étape applique les valeurs recommandées de manière persistante.

sudo tee /etc/sysctl.d/99-sonarqube.conf <<EOF
vm.max_map_count=524288
fs.file-max=131072
EOF
sudo sysctl --system

sudo tee /etc/security/limits.d/99-sonarqube.conf <<EOF
sonarqube   -   nofile   131072
sonarqube   -   nproc    8192
EOF

Le premier fichier déclare deux paramètres noyau, le second relève les limites par utilisateur. Après sysctl --system, vérifier la valeur appliquée avec sysctl vm.max_map_count qui doit afficher 524288. Si la machine est partagée avec d’autres services, ces valeurs sont sans effet négatif : elles relèvent un plafond, jamais un plancher. Sur une machine de laboratoire ponctuelle, on peut éviter ces ajustements en passant SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true dans le compose, mais cette option est explicitement déconseillée en production par SonarSource.

Étape 2 — Écrire le fichier docker-compose.yml

Le pattern recommandé combine deux services : sonarqube (image officielle sonarqube:community) et une base postgres:17-alpine. La séparation des préoccupations permet de redémarrer SonarQube sans perdre les données, de sauvegarder la base indépendamment, et de monter un PostgreSQL managé externe le jour où la charge le justifie. Créer un dossier dédié et y placer le fichier suivant.

services:
  sonarqube:
    image: sonarqube:community
    depends_on:
      - db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonarqube
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: ${SONAR_DB_PASSWORD}
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_logs:/opt/sonarqube/logs
      - sonarqube_extensions:/opt/sonarqube/extensions
    ports:
      - "127.0.0.1:9000:9000"
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
    restart: unless-stopped

  db:
    image: postgres:17-alpine
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: ${SONAR_DB_PASSWORD}
      POSTGRES_DB: sonarqube
    volumes:
      - postgresql_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  sonarqube_data:
  sonarqube_logs:
  sonarqube_extensions:
  postgresql_data:

Trois points méritent l’attention. D’abord le port 9000 est lié explicitement à 127.0.0.1 et non à 0.0.0.0 : seul le reverse proxy local accédera au backend, jamais l’Internet ouvert. Ensuite les volumes nommés isolent les données persistantes des couches d’image, ce qui rend les mises à jour mensuelles sans douleur (un docker compose pull && docker compose up -d suffit). Enfin, la variable SONAR_DB_PASSWORD est lue depuis un fichier .env qu’il faut générer immédiatement.

Étape 3 — Générer le mot de passe et lancer la stack

Le mot de passe doit être suffisamment long pour résister à un brute force par dictionnaire et stocké hors du dépôt Git. openssl fournit un générateur fiable.

cat > .env <<EOF
SONAR_DB_PASSWORD=$(openssl rand -base64 32 | tr -d '+/=')
EOF
chmod 600 .env

docker compose up -d
docker compose logs -f sonarqube

Le démarrage initial prend deux à trois minutes. Le journal affiche d’abord la migration de schéma PostgreSQL, puis l’initialisation d’Elasticsearch, puis enfin la ligne SonarQube is operational. Si le conteneur boucle en redémarrage, vérifier les logs avec docker compose logs sonarqube | grep -i bootstrap ; les erreurs max virtual memory areas trahissent un échec de l’étape 1. Couper avec Ctrl+C dès l’apparition du message operational, le service continue à tourner en arrière-plan.

Étape 4 — Configurer Nginx en reverse proxy avec TLS

Exposer le port 9000 directement sur Internet serait une faute : pas de TLS, pas de WAF, pas de protection DDoS. Un Nginx local termine le TLS, redirige vers le backend, et permet d’ajouter rate limiting, IP whitelisting ou authentification HTTP basique selon les besoins. Installer le package puis créer le vhost.

sudo apt install -y nginx certbot python3-certbot-nginx
sudo certbot --nginx -d sonar.example.com --redirect --agree-tos -m admin@example.com -n

Certbot crée et gère le certificat Let’s Encrypt automatiquement, puis injecte le bloc TLS dans la configuration Nginx. Reste à compléter le vhost pour pointer sur SonarQube avec les bons headers proxy, sans quoi les liens internes générés par l’application seront cassés et le WebSocket de mise à jour live ne fonctionnera pas.

server {
    listen 443 ssl http2;
    server_name sonar.example.com;
    ssl_certificate     /etc/letsencrypt/live/sonar.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sonar.example.com/privkey.pem;
    client_max_body_size 50M;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

La directive client_max_body_size 50M autorise les téléversements d’extensions plugin volumineuses. Tester la syntaxe avec sudo nginx -t puis recharger avec sudo systemctl reload nginx. Une requête curl -I https://sonar.example.com/api/system/status doit retourner un HTTP/2 200 et un JSON {"id":"...","status":"UP"}.

Étape 5 — Première connexion et changement du mot de passe admin

L’instance fraîchement installée accepte les identifiants par défaut admin / admin. Le serveur impose immédiatement de changer ce mot de passe à la première connexion, mesure introduite à partir de la 8.x pour éviter les instances exposées avec les credentials par défaut. Ouvrir https://sonar.example.com dans le navigateur, s’authentifier, choisir un mot de passe long stocké dans un gestionnaire d’équipe (Bitwarden, 1Password, Vaultwarden self-hosted).

Profiter de la première session pour activer la force l’authentification dans Administration → Configuration → Security, désactiver la création d’instances anonymes, et créer un projet de démonstration. Pour un environnement multi-équipe, configurer immédiatement le SSO via le plugin OIDC officiel ou via Authentik si l’organisation héberge un IdP interne.

Étape 6 — Lancer le premier scan d’un projet

Le scanner est un binaire Java distribué séparément du serveur. Il analyse le projet localement et pousse les résultats vers le serveur via API. Sur le poste de développement ou le runner CI, télécharger la dernière release depuis le repository officiel.

cd /opt
sudo curl -sLO https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-8.1.0.6389-linux-x64.zip
sudo unzip sonar-scanner-cli-8.1.0.6389-linux-x64.zip
sudo ln -s /opt/sonar-scanner-8.1.0.6389-linux-x64/bin/sonar-scanner /usr/local/bin/sonar-scanner
sonar-scanner --version

La sortie doit afficher SonarScanner 7.0.x.xxxx, la version Java détectée (≥ 21) et le système d’exploitation. Si la commande échoue avec UnsupportedClassVersionError, c’est que la JRE installée est trop ancienne ; installer Temurin 21 avec sudo apt install -y temurin-21-jre après ajout du dépôt Eclipse Adoptium. Côté serveur Sonar, créer ensuite un token utilisateur dans My Account → Security → Generate Tokens et le copier dans une variable d’environnement.

cd /chemin/vers/projet
cat > sonar-project.properties <<EOF
sonar.projectKey=mon-projet
sonar.projectName=Mon Projet
sonar.projectVersion=1.0
sonar.sources=src
sonar.sourceEncoding=UTF-8
EOF

export SONAR_TOKEN=sqp_xxxxxxxxxxxx
sonar-scanner -Dsonar.host.url=https://sonar.example.com

Le scan dure quelques dizaines de secondes pour un petit projet, plusieurs minutes pour un monorepo de 100 000 lignes. À la fin, le scanner affiche l’URL du Quality Gate ; cliquer pour atterrir sur le dashboard de l’analyse, qui agrège bugs, vulnérabilités, code smells, hotspots de sécurité et duplication. Le premier passage révèle souvent des dizaines de findings sur un projet existant — ne pas chercher à tout corriger d’un coup, prioriser les blocker et critical en sécurité d’abord.

Étape 7 — Décorer les pull requests GitHub ou GitLab

La valeur opérationnelle de SonarQube se révèle quand chaque pull request reçoit automatiquement un commentaire avec les nouveaux findings introduits par le diff. En Community Build, cette fonctionnalité passe par le plugin tiers mc1arke/sonarqube-community-branch-plugin, non maintenu par SonarSource mais largement utilisé. Le plugin se télécharge depuis ses releases GitHub et se place dans extensions/plugins du serveur.

SONAR_VOL=$(docker volume inspect sonarqube_extensions -f '{{.Mountpoint}}')
PLUGIN_VER=26.4.0
sudo curl -sL -o "${SONAR_VOL}/plugins/sonarqube-community-branch-plugin.jar" \
  https://github.com/mc1arke/sonarqube-community-branch-plugin/releases/download/${PLUGIN_VER}/sonarqube-community-branch-plugin-${PLUGIN_VER}.jar
docker compose restart sonarqube

Après redémarrage, le menu Administration → DevOps Platform Integrations propose d’ajouter une connexion GitHub App ou un token GitLab. Configurer la connexion, lier le projet au dépôt distant, puis relancer le scan en passant les paramètres de PR au scanner avec -Dsonar.pullrequest.key=42 -Dsonar.pullrequest.branch=feat/xyz -Dsonar.pullrequest.base=main. Le commentaire automatique apparaîtra sous quelques secondes sur la PR, avec ligne par ligne les nouveaux problèmes détectés.

Étape 8 — Sauvegarder et mettre à jour

Une instance auto-hébergée vit ou meurt par sa stratégie de sauvegarde. PostgreSQL contient toutes les analyses historiques, les utilisateurs, les permissions ; les volumes sonarqube_data et sonarqube_extensions contiennent les index Elasticsearch et les plugins. Un script cron quotidien suffit.

cat > /usr/local/bin/sonar-backup.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
DATE=$(date +%F)
BACKUP=/var/backups/sonar
mkdir -p "$BACKUP"
docker compose -f /opt/sonar/docker-compose.yml exec -T db \
  pg_dump -U sonar sonarqube | gzip > "$BACKUP/sonarqube-${DATE}.sql.gz"
find "$BACKUP" -mtime +14 -delete
EOF
chmod +x /usr/local/bin/sonar-backup.sh
echo '15 3 * * * root /usr/local/bin/sonar-backup.sh' | sudo tee /etc/cron.d/sonar-backup

Le script dump la base toutes les nuits à 3h15, conserve 14 jours d’historique, et utilise set -euo pipefail pour échouer dès la première erreur. Pour la mise à jour mensuelle de SonarQube, faire d’abord une sauvegarde manuelle, puis docker compose pull && docker compose up -d. La migration de schéma se déclenche automatiquement au démarrage du nouveau conteneur ; si elle échoue, restaurer le dump avec gunzip -c sonarqube-2026-05-06.sql.gz | docker compose exec -T db psql -U sonar sonarqube.

Erreurs fréquentes

Symptôme Cause Solution
Conteneur en boucle de redémarrage avec bootstrap checks failed vm.max_map_count non appliqué Vérifier sysctl vm.max_map_count, refaire l’étape 1 et redémarrer le service Docker
UI accessible mais blanche, console JS pleine d’erreurs mixed content Header X-Forwarded-Proto manquant côté Nginx Ajouter proxy_set_header X-Forwarded-Proto https; et recharger
Scanner échoue avec UnsupportedClassVersionError JRE 8 ou 11 alors que 21 est requise depuis 2024 Installer Temurin 21 et exporter JAVA_HOME
Quality Gate toujours rouge sur les anciens projets Mode new code pas configuré, toute la dette historique pèse Project Settings → New Code : définir une référence à partir d’une branche ou d’une date, focus sur le nouveau code
Décoration PR ne se déclenche pas Mauvais sonar.pullrequest.key côté pipeline CI Vérifier que la variable d’environnement de la PR est correctement passée au scanner
Lenteur croissante après quelques mois Index Elasticsearch saturé Administration → System → Background Tasks : déclencher un Project Reindex, augmenter SONAR_WEB_JAVAOPTS=-Xmx2g

FAQ

SonarQube Community Build supporte-t-il les analyses différentielles sur les branches non-main ? Non en natif : la branch analysis et la décoration de pull request sont des fonctionnalités de l’édition Developer (payante). Pour les obtenir gratuitement, l’écosystème s’appuie sur le plugin tiers mc1arke/sonarqube-community-branch-plugin, non maintenu par SonarSource mais largement utilisé. À défaut, le mode main only reste fonctionnel pour suivre la dette globale.

Quelle taille mémoire JVM allouer en production ? Le défaut -Xmx2g sur le serveur web et -Xmx2g sur le compute engine convient jusqu’à 10 millions de lignes de code analysées. Au-delà, monter à 4 Go via SONAR_WEB_JAVAOPTS et SONAR_CE_JAVAOPTS. Surveiller la consommation réelle dans System Info.

Peut-on remplacer PostgreSQL par MySQL ou MariaDB ? Non, le support MySQL a été retiré en version 7.9 et MariaDB n’a jamais été officiellement supporté. PostgreSQL ≥ 13 est obligatoire ; la version 17 (sortie en septembre 2024, supportée par la communauté jusqu’en 2029) est recommandée pour les nouvelles installations.

Les plugins tiers fonctionnent-ils en Community Build ? Oui, à condition qu’ils ciblent une API serveur compatible. Vérifier la matrice de compatibilité du plugin avant installation. La fonctionnalité Marketplace intégrée gère elle-même cette vérification.

Comment exporter les findings vers DefectDojo ? SonarQube ne produit pas nativement de SARIF en sortie d’analyse — l’option sonar.sarifReportPaths sert au sens inverse, à importer du SARIF dans Sonar. Pour pousser les findings Sonar vers DefectDojo, utiliser l’API REST Sonar (GET /api/issues/search en JSON) puis ingérer dans DefectDojo via son endpoint /api/v2/import-scan/ avec le scanner type Sonarqube API Import qui parse nativement ce format.

Tutoriels associés

Lectures recommandées

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité