Set up SonarQube Community Edition with PostgreSQL database backend, SSL/TLS encryption, and CI/CD integration for comprehensive static code analysis and security scanning.
Prerequisites
- Server with 4GB+ RAM
- Domain name with DNS access
- Sudo privileges
- Basic PostgreSQL knowledge
What this solves
SonarQube provides automated code quality analysis, security vulnerability detection, and technical debt measurement for your development projects. This tutorial sets up a production-ready SonarQube instance with PostgreSQL database backend, SSL encryption via reverse proxy, and integration points for CI/CD pipelines.
Step-by-step installation
Update system packages and install prerequisites
Start by updating your system and installing Java 17, which is required for SonarQube 10.x.
sudo apt update && sudo apt upgrade -y
sudo apt install -y openjdk-17-jre-headless wget unzip postgresql postgresql-contrib nginx certbot python3-certbot-nginx
Configure system limits for SonarQube
SonarQube requires specific kernel parameters and file limits to operate efficiently with Elasticsearch.
vm.max_map_count=524288
fs.file-max=131072
sudo sysctl -p /etc/sysctl.d/99-sonarqube.conf
sonarqube - nofile 131072
sonarqube - nproc 8192
Initialize PostgreSQL and create SonarQube database
Set up PostgreSQL database server and create a dedicated database and user for SonarQube.
sudo systemctl enable --now postgresql
sudo systemctl status postgresql
sudo -u postgres createuser sonarqube
sudo -u postgres createdb -O sonarqube sonarqube
sudo -u postgres psql -c "ALTER USER sonarqube WITH ENCRYPTED PASSWORD 'SecureP@ssw0rd123';"
Create SonarQube system user
Create a dedicated system user to run SonarQube services securely without root privileges.
sudo useradd --system --no-create-home --shell /bin/false sonarqube
sudo mkdir -p /opt/sonarqube /var/log/sonarqube
sudo chown sonarqube:sonarqube /opt/sonarqube /var/log/sonarqube
Download and install SonarQube Community Edition
Download the latest SonarQube Community Edition and extract it to the installation directory.
cd /tmp
wget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-10.3.0.82913.zip
unzip sonarqube-10.3.0.82913.zip
sudo mv sonarqube-10.3.0.82913/* /opt/sonarqube/
sudo chown -R sonarqube:sonarqube /opt/sonarqube
Configure SonarQube database connection
Configure SonarQube to connect to your PostgreSQL database with proper authentication.
# Database Configuration
sonar.jdbc.username=sonarqube
sonar.jdbc.password=SecureP@ssw0rd123
sonar.jdbc.url=jdbc:postgresql://localhost:5432/sonarqube
Web Server Configuration
sonar.web.host=127.0.0.1
sonar.web.port=9000
sonar.web.context=/
System Configuration
sonar.path.data=/opt/sonarqube/data
sonar.path.temp=/opt/sonarqube/temp
sonar.path.logs=/var/log/sonarqube
Security
sonar.forceAuthentication=true
sonar.security.realm=sonar
sudo chown sonarqube:sonarqube /opt/sonarqube/conf/sonar.properties
sudo chmod 640 /opt/sonarqube/conf/sonar.properties
Create SonarQube systemd service
Set up a systemd service to manage SonarQube startup, shutdown, and automatic restart.
[Unit]
Description=SonarQube service
After=syslog.target network.target postgresql.service
Requires=postgresql.service
[Service]
Type=forking
ExecStart=/opt/sonarqube/bin/linux-x86-64/sonar.sh start
ExecStop=/opt/sonarqube/bin/linux-x86-64/sonar.sh stop
User=sonarqube
Group=sonarqube
Restart=always
LimitNOFILE=131072
LimitNPROC=8192
TimeoutStartSec=5
SuccessExitStatus=143
KillMode=mixed
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable sonarqube
Configure NGINX reverse proxy with SSL
Set up NGINX as a reverse proxy to handle SSL termination and serve SonarQube over HTTPS.
server {
listen 80;
server_name sonar.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name sonar.example.com;
# SSL Configuration (will be populated by certbot)
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
# Proxy Configuration
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $http_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 $scheme;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
client_max_body_size 50m;
}
# Logs
access_log /var/log/nginx/sonarqube.access.log;
error_log /var/log/nginx/sonarqube.error.log;
}
sudo ln -s /etc/nginx/sites-available/sonarqube /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl enable --now nginx
Obtain SSL certificate with Let's Encrypt
Use Certbot to automatically obtain and configure SSL certificates for your domain.
sudo certbot --nginx -d sonar.example.com
sudo systemctl enable certbot.timer
Configure firewall rules
Open necessary ports for HTTP/HTTPS traffic while keeping the SonarQube port (9000) internal.
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
sudo ufw status
Start SonarQube service
Start SonarQube and monitor the startup process to ensure successful initialization.
sudo systemctl start sonarqube
sudo systemctl status sonarqube
sudo tail -f /var/log/sonarqube/sonar.log
Configure LDAP authentication (optional)
Integrate SonarQube with your existing LDAP/Active Directory for centralized user management.
# LDAP Configuration
sonar.security.realm=LDAP
ldap.url=ldaps://ldap.example.com:636
ldap.bindDn=cn=sonar,ou=users,dc=example,dc=com
ldap.bindPassword=LdapBindPassword123
User Configuration
ldap.user.baseDn=ou=users,dc=example,dc=com
ldap.user.request=(&(objectClass=inetOrgPerson)(uid={login}))
ldap.user.realNameAttribute=cn
ldap.user.emailAttribute=mail
Group Configuration
ldap.group.baseDn=ou=groups,dc=example,dc=com
ldap.group.request=(&(objectClass=groupOfUniqueNames)(uniqueMember={dn}))
ldap.group.idAttribute=cn
Configure performance settings
Optimize SonarQube performance based on your server resources and expected usage.
# Performance Tuning
sonar.web.javaOpts=-Xmx2g -Xms1g -XX:+HeapDumpOnOutOfMemoryError
sonar.ce.javaOpts=-Xmx2g -Xms1g -XX:+HeapDumpOnOutOfMemoryError
sonar.search.javaOpts=-Xmx1g -Xms1g -XX:MaxDirectMemorySize=512m -XX:+HeapDumpOnOutOfMemoryError
Database Connection Pool
sonar.jdbc.maxActive=60
sonar.jdbc.maxIdle=5
sonar.jdbc.minIdle=2
sonar.jdbc.maxWait=5000
sudo systemctl restart sonarqube
Set up backup automation
Create automated backup scripts for SonarQube configuration and PostgreSQL database.
#!/bin/bash
BACKUP_DIR="/backup/sonarqube"
DATE=$(date +%Y%m%d_%H%M%S)
Create backup directory
mkdir -p "$BACKUP_DIR"
Backup PostgreSQL database
pg_dump -U sonarqube -h localhost sonarqube | gzip > "$BACKUP_DIR/sonarqube_db_$DATE.sql.gz"
Backup SonarQube configuration
tar -czf "$BACKUP_DIR/sonarqube_config_$DATE.tar.gz" /opt/sonarqube/conf/
Cleanup old backups (keep 30 days)
find "$BACKUP_DIR" -name "sonarqube_*" -mtime +30 -delete
echo "SonarQube backup completed: $DATE"
sudo chmod +x /opt/backup-sonarqube.sh
sudo chown root:root /opt/backup-sonarqube.sh
sudo crontab -e
# Daily SonarQube backup at 2 AM
0 2 * /opt/backup-sonarqube.sh >> /var/log/sonarqube-backup.log 2>&1
Verify your setup
Test your SonarQube installation and verify all components are working correctly.
# Check service status
sudo systemctl status sonarqube postgresql nginx
Test database connection
sudo -u postgres psql -d sonarqube -c "SELECT version();"
Check SonarQube logs
sudo tail -20 /var/log/sonarqube/sonar.log
Test web interface
curl -I https://sonar.example.com
Verify SSL certificate
echo | openssl s_client -connect sonar.example.com:443 -servername sonar.example.com 2>/dev/null | openssl x509 -noout -dates
Access your SonarQube instance at https://sonar.example.com and log in with the default credentials: username admin, password admin. You'll be prompted to change the password on first login.
CI/CD integration examples
GitLab CI integration
Configure GitLab CI to run SonarQube analysis on code commits and merge requests.
sonarqube-check:
stage: test
image:
name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""]
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: "0"
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- sonar-scanner
-Dsonar.projectKey=${CI_PROJECT_NAME}
-Dsonar.sources=.
-Dsonar.host.url=https://sonar.example.com
-Dsonar.login=${SONAR_TOKEN}
allow_failure: true
only:
- merge_requests
- master
- develop
Jenkins integration
Set up Jenkins pipeline to integrate SonarQube analysis with your build process.
pipeline {
agent any
environment {
SONAR_TOKEN = credentials('sonar-token')
}
stages {
stage('Build') {
steps {
// Your build steps
sh 'mvn clean compile'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
}
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar -Dsonar.projectKey=my-project'
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 1, unit: 'HOURS') {
waitForQualityGate abortPipeline: true
}
}
}
}
}
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| SonarQube won't start | Insufficient memory or wrong Java version | Check java -version and increase heap size in sonar.properties |
| Database connection failed | Wrong credentials or PostgreSQL not running | Verify PostgreSQL status: sudo systemctl status postgresql |
| 502 Bad Gateway on web interface | SonarQube service not responding | Check logs: sudo tail -f /var/log/sonarqube/sonar.log |
| SSL certificate errors | Domain mismatch or certificate expired | Run sudo certbot renew and verify domain configuration |
| LDAP authentication not working | Wrong LDAP configuration or network issues | Test LDAP connection with ldapsearch tool |
| Analysis fails with permission errors | Scanner can't write to temp directories | Ensure CI runner has write access to project directory |
| Out of memory during analysis | Large codebase exceeds default limits | Increase scanner memory: -Xmx2g in scanner options |
Next steps
- Install and configure GitLab CE with CI/CD runners for complete DevOps pipeline
- Install and configure Nexus Repository Manager for artifact management
- Configure SonarQube quality gates and custom rules for advanced code quality enforcement
- Setup SonarQube scanner for multiple programming languages
- Integrate SonarQube with Kubernetes deployments
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration variables
SONAR_VERSION="10.3.0.82913"
SONAR_URL="https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${SONAR_VERSION}.zip"
SONAR_USER="sonarqube"
SONAR_HOME="/opt/sonarqube"
SONAR_LOG_DIR="/var/log/sonarqube"
DB_NAME="sonarqube"
DB_USER="sonarqube"
DB_PASS="SecureP@ssw0rd123"
DOMAIN=""
# Usage message
usage() {
echo "Usage: $0 [domain]"
echo " domain: Optional domain name for SSL configuration (e.g., sonar.example.com)"
exit 1
}
# Error handling
cleanup() {
echo -e "${RED}[ERROR] Installation failed. Cleaning up...${NC}"
systemctl stop sonarqube 2>/dev/null || true
systemctl stop postgresql 2>/dev/null || true
userdel $SONAR_USER 2>/dev/null || true
rm -rf $SONAR_HOME $SONAR_LOG_DIR /tmp/sonarqube-* 2>/dev/null || true
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Parse arguments
if [[ $# -gt 1 ]]; then
usage
elif [[ $# -eq 1 ]]; then
DOMAIN="$1"
fi
# Detect distribution
echo -e "${YELLOW}[1/10] Detecting distribution...${NC}"
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update && apt upgrade -y"
PKG_INSTALL="apt install -y"
JAVA_PKG="openjdk-17-jre-headless"
NGINX_CONF_DIR="/etc/nginx/sites-available"
NGINX_ENABLED_DIR="/etc/nginx/sites-enabled"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
JAVA_PKG="java-17-openjdk-headless"
NGINX_CONF_DIR="/etc/nginx/conf.d"
NGINX_ENABLED_DIR="/etc/nginx/conf.d"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
JAVA_PKG="java-17-openjdk-headless"
NGINX_CONF_DIR="/etc/nginx/conf.d"
NGINX_ENABLED_DIR="/etc/nginx/conf.d"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect distribution${NC}"
exit 1
fi
echo -e "${GREEN}Detected: $PRETTY_NAME${NC}"
# Update system and install prerequisites
echo -e "${YELLOW}[2/10] Installing prerequisites...${NC}"
$PKG_UPDATE
$PKG_INSTALL $JAVA_PKG wget unzip postgresql postgresql-contrib nginx certbot python3-certbot-nginx
# Install postgresql-server for RHEL-based systems
if [[ "$PKG_MGR" == "dnf" || "$PKG_MGR" == "yum" ]]; then
$PKG_INSTALL postgresql-server
fi
# Configure system limits
echo -e "${YELLOW}[3/10] Configuring system limits...${NC}"
cat > /etc/sysctl.d/99-sonarqube.conf << 'EOF'
vm.max_map_count=524288
fs.file-max=131072
EOF
sysctl -p /etc/sysctl.d/99-sonarqube.conf
cat >> /etc/security/limits.conf << 'EOF'
sonarqube - nofile 131072
sonarqube - nproc 8192
EOF
# Initialize PostgreSQL
echo -e "${YELLOW}[4/10] Initializing PostgreSQL...${NC}"
if [[ "$PKG_MGR" == "dnf" || "$PKG_MGR" == "yum" ]]; then
postgresql-setup --initdb 2>/dev/null || true
fi
systemctl enable postgresql
systemctl start postgresql
# Create SonarQube database and user
echo -e "${YELLOW}[5/10] Creating SonarQube database...${NC}"
sudo -u postgres createuser $DB_USER 2>/dev/null || true
sudo -u postgres createdb -O $DB_USER $DB_NAME 2>/dev/null || true
sudo -u postgres psql -c "ALTER USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_PASS';" 2>/dev/null || true
# Create SonarQube system user
echo -e "${YELLOW}[6/10] Creating SonarQube system user...${NC}"
useradd --system --no-create-home --shell /bin/false $SONAR_USER 2>/dev/null || true
mkdir -p $SONAR_HOME $SONAR_LOG_DIR
chown $SONAR_USER:$SONAR_USER $SONAR_HOME $SONAR_LOG_DIR
# Download and install SonarQube
echo -e "${YELLOW}[7/10] Downloading and installing SonarQube...${NC}"
cd /tmp
wget -q $SONAR_URL -O sonarqube-${SONAR_VERSION}.zip
unzip -q sonarqube-${SONAR_VERSION}.zip
mv sonarqube-${SONAR_VERSION}/* $SONAR_HOME/
chown -R $SONAR_USER:$SONAR_USER $SONAR_HOME
# Configure SonarQube
echo -e "${YELLOW}[8/10] Configuring SonarQube...${NC}"
cat > $SONAR_HOME/conf/sonar.properties << EOF
# Database Configuration
sonar.jdbc.username=$DB_USER
sonar.jdbc.password=$DB_PASS
sonar.jdbc.url=jdbc:postgresql://localhost:5432/$DB_NAME
# Web Server Configuration
sonar.web.host=127.0.0.1
sonar.web.port=9000
sonar.web.context=/
# System Configuration
sonar.path.data=$SONAR_HOME/data
sonar.path.temp=$SONAR_HOME/temp
sonar.path.logs=$SONAR_LOG_DIR
# Security
sonar.forceAuthentication=true
sonar.security.realm=sonar
EOF
chown $SONAR_USER:$SONAR_USER $SONAR_HOME/conf/sonar.properties
chmod 640 $SONAR_HOME/conf/sonar.properties
# Create systemd service
echo -e "${YELLOW}[9/10] Creating systemd service...${NC}"
cat > /etc/systemd/system/sonarqube.service << EOF
[Unit]
Description=SonarQube service
After=syslog.target network.target postgresql.service
Requires=postgresql.service
[Service]
Type=forking
ExecStart=$SONAR_HOME/bin/linux-x86-64/sonar.sh start
ExecStop=$SONAR_HOME/bin/linux-x86-64/sonar.sh stop
User=$SONAR_USER
Group=$SONAR_USER
Restart=always
LimitNOFILE=131072
LimitNPROC=8192
TimeoutStartSec=300
SuccessExitStatus=143
KillMode=mixed
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable sonarqube
# Configure NGINX
echo -e "${YELLOW}[10/10] Configuring NGINX reverse proxy...${NC}"
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
NGINX_CONF="$NGINX_CONF_DIR/sonarqube"
else
NGINX_CONF="$NGINX_CONF_DIR/sonarqube.conf"
fi
if [[ -n "$DOMAIN" ]]; then
cat > $NGINX_CONF << EOF
server {
listen 80;
server_name $DOMAIN;
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name $DOMAIN;
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 \$scheme;
}
client_max_body_size 50M;
}
EOF
else
cat > $NGINX_CONF << EOF
server {
listen 80;
server_name _;
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 \$scheme;
}
client_max_body_size 50M;
}
EOF
fi
# Enable site for Ubuntu/Debian
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
ln -sf $NGINX_CONF $NGINX_ENABLED_DIR/sonarqube
fi
# Start services
systemctl enable nginx
systemctl start nginx
systemctl start sonarqube
# Configure firewall
if command -v ufw >/dev/null 2>&1; then
ufw allow 80/tcp
ufw allow 443/tcp
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
fi
# Clean up
rm -rf /tmp/sonarqube-*
# Verification
echo -e "${GREEN}Installation completed successfully!${NC}"
echo -e "${YELLOW}Verifying installation...${NC}"
# Wait for SonarQube to start
echo "Waiting for SonarQube to start..."
sleep 30
# Check services
if systemctl is-active --quiet postgresql; then
echo -e "${GREEN}✓ PostgreSQL is running${NC}"
else
echo -e "${RED}✗ PostgreSQL is not running${NC}"
fi
if systemctl is-active --quiet nginx; then
echo -e "${GREEN}✓ NGINX is running${NC}"
else
echo -e "${RED}✗ NGINX is not running${NC}"
fi
if systemctl is-active --quiet sonarqube; then
echo -e "${GREEN}✓ SonarQube is running${NC}"
else
echo -e "${RED}✗ SonarQube is not running${NC}"
fi
# Display access information
echo -e "\n${GREEN}SonarQube Installation Summary:${NC}"
echo "================================"
if [[ -n "$DOMAIN" ]]; then
echo "URL: https://$DOMAIN"
echo "Run 'certbot --nginx -d $DOMAIN' to obtain SSL certificate"
else
echo "URL: http://$(hostname -I | awk '{print $1}')"
fi
echo "Default credentials: admin/admin"
echo "Database: PostgreSQL ($DB_NAME)"
echo "Logs: $SONAR_LOG_DIR"
Review the script before running. Execute with: bash install.sh