Deploy SonarQube on Kubernetes using Helm charts with PostgreSQL database, configure automated code scanning workflows, and implement persistent storage for comprehensive code quality analysis in your CI/CD pipeline.
Prerequisites
- Kubernetes cluster (1.24+)
- kubectl configured
- Helm 3.8+
- 4GB RAM minimum
- 20GB storage available
What this solves
SonarQube integration with Kubernetes provides automated code quality scanning for your applications directly in your container orchestration platform. This setup enables continuous code analysis, quality gates, and security vulnerability detection as part of your CI/CD workflows. You'll have a scalable, production-ready SonarQube deployment with persistent storage and database backend.
Step-by-step installation
Update system packages and install prerequisites
Start by updating your system and installing required tools including kubectl, helm, and curl for managing your Kubernetes cluster and deployments.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget gnupg software-properties-common
Install kubectl and verify Kubernetes cluster access
Install the Kubernetes command-line tool and verify you can access your cluster. This assumes you have an existing Kubernetes cluster configured.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
kubectl version --client
kubectl cluster-info
Install Helm 3
Install Helm package manager for Kubernetes to deploy SonarQube using official charts. This provides a streamlined deployment process with customizable configurations.
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt update
sudo apt install -y helm
helm version
Create SonarQube namespace and add Helm repository
Create a dedicated namespace for SonarQube deployment and add the official SonarQube Helm repository for accessing the latest charts.
kubectl create namespace sonarqube
helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube
helm repo update
helm search repo sonarqube
Create PostgreSQL database for SonarQube
Deploy PostgreSQL as the database backend for SonarQube using Helm. This provides persistent storage for code analysis data and configuration.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
auth:
postgresPassword: "SonarQubeP@ssw0rd!"
username: "sonarqube"
password: "SonarQubeDBP@ss!"
database: "sonarqube"
primary:
persistence:
enabled: true
size: 20Gi
storageClass: "standard"
resources:
requests:
memory: 256Mi
cpu: 250m
limits:
memory: 512Mi
cpu: 500m
helm install postgresql bitnami/postgresql -f postgresql-values.yaml -n sonarqube
Configure SonarQube Helm values
Create a comprehensive configuration file for SonarQube deployment including database connection, resource limits, and persistent storage settings.
postgresql:
enabled: false
jdbcOverwrite:
enable: true
jdbcUrl: "jdbc:postgresql://postgresql:5432/sonarqube"
jdbcUsername: "sonarqube"
jdbcPassword: "SonarQubeDBP@ss!"
sonarqube:
image:
tag: "10.3.0-community"
resources:
requests:
memory: 2Gi
cpu: 500m
limits:
memory: 4Gi
cpu: 1000m
persistence:
enabled: true
size: 10Gi
storageClass: "standard"
sonarProperties: |
sonar.forceAuthentication=true
sonar.security.realm=sonar
service:
type: LoadBalancer
ports:
http: 9000
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- name: sonarqube.example.com
path: /
tls:
- secretName: sonarqube-tls
hosts:
- sonarqube.example.com
plugins:
install:
- "https://github.com/dependency-check/dependency-check-sonar-plugin/releases/download/3.1.0/sonar-dependency-check-plugin-3.1.0.jar"
env:
- name: SONAR_ES_BOOTSTRAP_CHECKS_DISABLE
value: "true"
Deploy SonarQube with Helm
Install SonarQube using the configured values file. This creates all necessary Kubernetes resources including deployments, services, and persistent volumes.
helm install sonarqube sonarqube/sonarqube -f sonarqube-values.yaml -n sonarqube
kubectl get pods -n sonarqube -w
Configure SonarQube scanner deployment
Create a reusable SonarQube scanner pod configuration for running code analysis jobs. This template can be used in CI/CD pipelines for automated scanning.
apiVersion: v1
kind: ConfigMap
metadata:
name: sonar-scanner-config
namespace: sonarqube
data:
sonar-project.properties: |
sonar.projectKey=my-project
sonar.projectName=My Project
sonar.projectVersion=1.0
sonar.sources=src
sonar.language=java
sonar.java.binaries=target/classes
sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
---
apiVersion: batch/v1
kind: Job
metadata:
name: sonar-scanner-job
namespace: sonarqube
spec:
template:
spec:
containers:
- name: sonar-scanner
image: sonarsource/sonar-scanner-cli:5.0
env:
- name: SONAR_HOST_URL
value: "http://sonarqube-sonarqube:9000"
- name: SONAR_LOGIN
valueFrom:
secretKeyRef:
name: sonar-token
key: token
volumeMounts:
- name: source-code
mountPath: /usr/src
- name: scanner-config
mountPath: /opt/sonar-scanner/conf/sonar-project.properties
subPath: sonar-project.properties
workingDir: /usr/src
command: ["sonar-scanner"]
volumes:
- name: source-code
persistentVolumeClaim:
claimName: source-code-pvc
- name: scanner-config
configMap:
name: sonar-scanner-config
restartPolicy: Never
kubectl apply -f sonar-scanner-pod.yaml
Create persistent volume for source code
Set up persistent storage for source code that will be analyzed. This allows CI/CD systems to mount code repositories for analysis.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: source-code-pvc
namespace: sonarqube
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: standard
kubectl apply -f source-code-pvc.yaml
kubectl get pvc -n sonarqube
Configure RBAC for scanner pods
Create service account and RBAC permissions for SonarQube scanner pods to interact with Kubernetes API when needed for advanced integrations.
apiVersion: v1
kind: ServiceAccount
metadata:
name: sonar-scanner
namespace: sonarqube
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: sonarqube
name: sonar-scanner-role
rules:
- apiGroups: [""]
resources: ["pods", "configmaps", "secrets"]
verbs: ["get", "list"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["get", "list", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: sonar-scanner-binding
namespace: sonarqube
subjects:
- kind: ServiceAccount
name: sonar-scanner
namespace: sonarqube
roleRef:
kind: Role
name: sonar-scanner-role
apiGroup: rbac.authorization.k8s.io
kubectl apply -f scanner-rbac.yaml
Set up automated scanning workflow
Create a sample CI/CD integration script that demonstrates how to trigger SonarQube analysis from your build pipeline using Kubernetes jobs.
#!/bin/bash
CI/CD Integration Script for SonarQube Scanning
PROJECT_KEY="${1:-my-project}"
PROJECT_NAME="${2:-My Project}"
SOURCE_PATH="${3:-/tmp/source}"
SONAR_TOKEN="${4}"
if [ -z "$SONAR_TOKEN" ]; then
echo "Error: SONAR_TOKEN environment variable is required"
exit 1
fi
Create secret for SonarQube token
kubectl create secret generic sonar-token \
--from-literal=token="$SONAR_TOKEN" \
-n sonarqube \
--dry-run=client -o yaml | kubectl apply -f -
Update scanner job with project details
cat > /tmp/scanner-job.yaml << EOF
apiVersion: batch/v1
kind: Job
metadata:
name: sonar-scan-$(date +%s)
namespace: sonarqube
spec:
template:
spec:
serviceAccountName: sonar-scanner
containers:
- name: sonar-scanner
image: sonarsource/sonar-scanner-cli:5.0
env:
- name: SONAR_HOST_URL
value: "http://sonarqube-sonarqube:9000"
- name: SONAR_LOGIN
valueFrom:
secretKeyRef:
name: sonar-token
key: token
- name: SONAR_PROJECT_KEY
value: "$PROJECT_KEY"
- name: SONAR_PROJECT_NAME
value: "$PROJECT_NAME"
volumeMounts:
- name: source-code
mountPath: /usr/src
workingDir: /usr/src
command:
- sonar-scanner
- "-Dsonar.projectKey=$PROJECT_KEY"
- "-Dsonar.projectName=$PROJECT_NAME"
- "-Dsonar.sources=."
volumes:
- name: source-code
persistentVolumeClaim:
claimName: source-code-pvc
restartPolicy: Never
EOF
Apply the job
kubectl apply -f /tmp/scanner-job.yaml
echo "SonarQube scan job created for project: $PROJECT_NAME"
Monitor job status
echo "Monitoring scan progress..."
kubectl wait --for=condition=complete job/sonar-scan-$(date +%s) -n sonarqube --timeout=600s
chmod +x scan-workflow.sh
Verify your setup
Check that all components are running correctly and SonarQube is accessible for code analysis.
# Check all pods are running
kubectl get pods -n sonarqube
Check services and endpoints
kubectl get svc -n sonarqube
Get SonarQube external IP or LoadBalancer status
kubectl get svc sonarqube-sonarqube -n sonarqube
Check persistent volumes
kubectl get pv,pvc -n sonarqube
View SonarQube logs
kubectl logs -l app=sonarqube -n sonarqube --tail=50
Test database connection
kubectl exec -it postgresql-0 -n sonarqube -- psql -U sonarqube -d sonarqube -c "SELECT version();"
Port forward to access SonarQube UI locally (if LoadBalancer not available)
kubectl port-forward svc/sonarqube-sonarqube 9000:9000 -n sonarqube
Access SonarQube at http://localhost:9000 (or your LoadBalancer IP) with default credentials admin/admin. Change the password immediately after first login.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| SonarQube pod stuck in pending | Insufficient resources or PVC not bound | kubectl describe pod -n sonarqube and check resource requests vs cluster capacity |
| Database connection failed | Wrong PostgreSQL credentials or service name | Verify postgresql service: kubectl get svc postgresql -n sonarqube |
| Scanner job fails with 401 | Invalid or missing SonarQube token | Generate new token in SonarQube UI: Administration > Security > Users > Tokens |
| Out of memory errors in scanner | Large codebase exceeds scanner memory | Increase scanner pod memory: resources.limits.memory: 4Gi in job spec |
| Persistent volumes not mounting | StorageClass not available or misconfigured | Check available storage classes: kubectl get storageclass |
| LoadBalancer IP pending forever | Cloud provider doesn't support LoadBalancer | Change service type to NodePort or configure Ingress controller |
Next steps
- Implement Kubernetes monitoring with Prometheus and Helm charts for comprehensive cluster observability
- Integrate SonarQube with Kubernetes security scanning workflows for continuous code quality analysis
- Set up SonarQube branch analysis with pull request decoration for enhanced code quality workflows
- Configure ArgoCD with SonarQube quality gates for GitOps deployment validation
- Setup Kubernetes Ingress NGINX with cert-manager for automated SSL certificates
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# SonarQube Kubernetes Installation Script
# Installs SonarQube with PostgreSQL on Kubernetes using Helm
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Default values
NAMESPACE="sonarqube"
POSTGRES_PASSWORD="SonarQubeP@ssw0rd!"
SONARQUBE_DB_PASSWORD="SonarQubeDBP@ss!"
DOMAIN="${1:-sonarqube.local}"
usage() {
echo "Usage: $0 [domain]"
echo "Example: $0 sonarqube.example.com"
exit 1
}
log_info() {
echo -e "${GREEN}$1${NC}"
}
log_warn() {
echo -e "${YELLOW}$1${NC}"
}
log_error() {
echo -e "${RED}$1${NC}"
}
cleanup() {
log_error "Installation failed. Cleaning up..."
kubectl delete namespace $NAMESPACE --ignore-not-found=true 2>/dev/null || true
helm repo remove sonarqube 2>/dev/null || true
helm repo remove bitnami 2>/dev/null || true
}
trap cleanup ERR
# Detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
GPG_DIR="/usr/share/keyrings"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
GPG_DIR="/etc/pki/rpm-gpg"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
GPG_DIR="/etc/pki/rpm-gpg"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
log_error "Cannot detect distribution"
exit 1
fi
# Check if running as root or with sudo
if [ "$EUID" -ne 0 ]; then
log_error "Please run as root or with sudo"
exit 1
fi
log_info "[1/8] Updating system packages and installing prerequisites..."
$PKG_UPDATE
if [ "$PKG_MGR" = "apt" ]; then
$PKG_INSTALL curl wget gnupg software-properties-common
else
$PKG_INSTALL curl wget gnupg2
fi
log_info "[2/8] Installing kubectl..."
KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl"
chmod 755 kubectl
mv kubectl /usr/local/bin/
# Verify kubectl installation
if ! kubectl version --client &>/dev/null; then
log_error "kubectl installation failed"
exit 1
fi
log_info "[3/8] Verifying Kubernetes cluster access..."
if ! kubectl cluster-info &>/dev/null; then
log_error "Cannot access Kubernetes cluster. Please ensure kubectl is configured correctly."
exit 1
fi
log_info "[4/8] Installing Helm..."
curl -fsSL https://baltocdn.com/helm/signing.asc | gpg --dearmor | tee $GPG_DIR/helm.gpg > /dev/null
chmod 644 $GPG_DIR/helm.gpg
if [ "$PKG_MGR" = "apt" ]; then
echo "deb [arch=$(dpkg --print-architecture) signed-by=$GPG_DIR/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | tee /etc/apt/sources.list.d/helm-stable-debian.list
chmod 644 /etc/apt/sources.list.d/helm-stable-debian.list
$PKG_UPDATE
$PKG_INSTALL helm
else
cat > /etc/yum.repos.d/helm-stable.repo << EOF
[helm]
name=Helm
baseurl=https://baltocdn.com/helm/stable/rpm/
enabled=1
gpgcheck=1
gpgkey=https://baltocdn.com/helm/signing.asc
EOF
chmod 644 /etc/yum.repos.d/helm-stable.repo
$PKG_INSTALL helm
fi
# Verify Helm installation
if ! helm version &>/dev/null; then
log_error "Helm installation failed"
exit 1
fi
log_info "[5/8] Creating SonarQube namespace and adding Helm repositories..."
kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
log_info "[6/8] Creating PostgreSQL configuration and deploying database..."
cat > /tmp/postgresql-values.yaml << EOF
auth:
postgresPassword: "$POSTGRES_PASSWORD"
username: "sonarqube"
password: "$SONARQUBE_DB_PASSWORD"
database: "sonarqube"
primary:
persistence:
enabled: true
size: 20Gi
resources:
requests:
memory: 256Mi
cpu: 250m
limits:
memory: 512Mi
cpu: 500m
EOF
chmod 644 /tmp/postgresql-values.yaml
helm upgrade --install postgresql bitnami/postgresql -f /tmp/postgresql-values.yaml -n $NAMESPACE
log_info "[7/8] Creating SonarQube configuration and deploying..."
cat > /tmp/sonarqube-values.yaml << EOF
postgresql:
enabled: false
jdbcOverwrite:
enable: true
jdbcUrl: "jdbc:postgresql://postgresql:5432/sonarqube"
jdbcUsername: "sonarqube"
jdbcPassword: "$SONARQUBE_DB_PASSWORD"
sonarqube:
image:
tag: "10.3.0-community"
resources:
requests:
memory: 2Gi
cpu: 500m
limits:
memory: 4Gi
cpu: 1000m
persistence:
enabled: true
size: 10Gi
sonarProperties: |
sonar.forceAuthentication=true
sonar.security.realm=sonar
service:
type: LoadBalancer
ports:
http: 9000
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
hosts:
- name: $DOMAIN
path: /
EOF
chmod 644 /tmp/sonarqube-values.yaml
helm upgrade --install sonarqube sonarqube/sonarqube -f /tmp/sonarqube-values.yaml -n $NAMESPACE
log_info "[8/8] Waiting for deployments and verifying installation..."
kubectl wait --for=condition=available --timeout=300s deployment/postgresql -n $NAMESPACE
kubectl wait --for=condition=available --timeout=600s deployment/sonarqube-sonarqube -n $NAMESPACE
# Clean up temporary files
rm -f /tmp/postgresql-values.yaml /tmp/sonarqube-values.yaml
log_info "✅ SonarQube installation completed successfully!"
log_info "📋 Installation Summary:"
echo " • Namespace: $NAMESPACE"
echo " • Domain: $DOMAIN"
echo " • Database: PostgreSQL"
echo ""
log_info "🔗 Access Information:"
echo " • Service: kubectl get svc -n $NAMESPACE"
echo " • Pods: kubectl get pods -n $NAMESPACE"
echo " • Logs: kubectl logs -f deployment/sonarqube-sonarqube -n $NAMESPACE"
echo ""
log_warn "⚠️ Default credentials: admin/admin (change on first login)"
log_warn "⚠️ Configure your ingress controller and DNS for external access"
Review the script before running. Execute with: bash install.sh