--- title: "How to Install SonarQube on Ubuntu 24.04 with PostgreSQL" description: "Install SonarQube Community Build on Ubuntu 24.04 with PostgreSQL, run it under systemd behind Nginx with HTTPS, and analyze your first project from GitLab CI." category: "CI/CD DevOps" language: en source: https://cubepath.com/docs/ci-cd-devops/sonarqube-installation-for-code-quality updated: 2026-09-25 --- # How to Install SonarQube on Ubuntu 24.04 with PostgreSQL SonarQube is a static analysis server that inspects source code for bugs, security vulnerabilities, code smells and duplicated code, and tracks test coverage over time. Its quality gates let your CI pipeline fail when new code does not meet the standard you define. In this tutorial you will install SonarQube Community Build on Ubuntu 24.04 with a PostgreSQL database, run it as a systemd service behind Nginx with a Let's Encrypt certificate, and analyze a first project from the command line and from GitLab CI. ## Prerequisites To follow this guide you need: - A server running Ubuntu 24.04 LTS on x86_64, for example a CubePath VPS, with at least 2 vCPUs and 4 GB of RAM. SonarQube runs three Java processes (web server, compute engine and an embedded Elasticsearch), so 2 GB is not enough in practice. - A non-root user with `sudo` privileges. - A domain name such as `sonar.your_domain` with a DNS A record pointing to your server's public IP address. - Ports 80 and 443 reachable from the internet for Let's Encrypt validation and HTTPS. Throughout the guide, replace `sonar.your_domain` with your own hostname and `your_strong_password` with a password you generate. ## Step 1 - Preparing the system SonarQube's embedded Elasticsearch refuses to start unless some kernel limits are raised. Create a sysctl file with the values SonarSource requires: ```bash sudo nano /etc/sysctl.d/99-sonarqube.conf ``` ```ini vm.max_map_count=524288 fs.file-max=131072 ``` Apply the settings and check them: ```bash sudo sysctl --system sysctl vm.max_map_count ``` ```output vm.max_map_count = 524288 ``` Install Java 17, `unzip` and the other tools used in this guide: ```bash sudo apt update sudo apt install openjdk-17-jre-headless unzip curl java -version ``` ```output openjdk version "17.0.x" ... OpenJDK Runtime Environment (build 17.0.x+...-Ubuntu-...) ``` ## Step 2 - Installing PostgreSQL and creating the database SonarQube needs an external database for anything beyond a quick test. Ubuntu 24.04 ships PostgreSQL 16, which SonarQube supports: ```bash sudo apt install postgresql sudo systemctl status postgresql ``` Open a PostgreSQL shell as the `postgres` superuser: ```bash sudo -u postgres psql ``` Create a dedicated role and a database owned by it. Replace `your_strong_password`: ```sql CREATE USER sonarqube WITH ENCRYPTED PASSWORD 'your_strong_password'; CREATE DATABASE sonarqube OWNER sonarqube; \q ``` Because `sonarqube` owns the database, it can create tables in its `public` schema without additional grants. Test the login over TCP, the same way SonarQube will connect: ```bash psql -h 127.0.0.1 -U sonarqube -d sonarqube -c "SELECT current_user;" ``` ```output current_user -------------- sonarqube (1 row) ``` ## Step 3 - Downloading SonarQube Create a system user that will own and run SonarQube. Elasticsearch refuses to run as root, so this user is mandatory: ```bash sudo useradd --system --home-dir /opt/sonarqube --shell /usr/sbin/nologin sonarqube ``` Check the latest Community Build version on the [SonarQube downloads page](https://www.sonarsource.com/products/sonarqube/downloads/) and set it in a variable. The version below is an example; replace it with the current one: ```bash SONAR_VERSION=25.1.0.102122 curl -fLO "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${SONAR_VERSION}.zip" ``` Extract the archive to `/opt/sonarqube` and give ownership to the new user: ```bash sudo unzip -q "sonarqube-${SONAR_VERSION}.zip" -d /opt sudo mv "/opt/sonarqube-${SONAR_VERSION}" /opt/sonarqube sudo chown -R sonarqube:sonarqube /opt/sonarqube ``` ## Step 4 - Configuring SonarQube The main configuration file is `/opt/sonarqube/conf/sonar.properties`. Every setting is present but commented out. Open it: ```bash sudo nano /opt/sonarqube/conf/sonar.properties ``` Find and uncomment (or add) the following lines. They point SonarQube to PostgreSQL and make the web server listen only on localhost, because Nginx will handle public traffic: ```ini sonar.jdbc.username=sonarqube sonar.jdbc.password=your_strong_password sonar.jdbc.url=jdbc:postgresql://localhost:5432/sonarqube sonar.web.host=127.0.0.1 sonar.web.port=9000 ``` The file now contains the database password, so restrict its permissions: ```bash sudo chmod 640 /opt/sonarqube/conf/sonar.properties ``` ## Step 5 - Creating a systemd service Create a unit file so SonarQube starts at boot and runs as the `sonarqube` user with the file and process limits Elasticsearch needs: ```bash sudo nano /etc/systemd/system/sonarqube.service ``` ```ini [Unit] Description=SonarQube After=network.target postgresql.service [Service] Type=forking User=sonarqube Group=sonarqube ExecStart=/opt/sonarqube/bin/linux-x86-64/sonar.sh start ExecStop=/opt/sonarqube/bin/linux-x86-64/sonar.sh stop LimitNOFILE=131072 LimitNPROC=8192 Restart=on-failure TimeoutStartSec=120 [Install] WantedBy=multi-user.target ``` Load the unit and start the service: ```bash sudo systemctl daemon-reload sudo systemctl enable --now sonarqube ``` The first start takes one or two minutes while SonarQube creates the database schema. Watch the main log until you see that it is operational: ```bash sudo tail -f /opt/sonarqube/logs/sonar.log ``` ```output ... INFO app[][o.s.a.SchedulerImpl] Process[web] is up ... INFO app[][o.s.a.SchedulerImpl] Process[ce] is up ... INFO app[][o.s.a.SchedulerImpl] SonarQube is operational ``` Press `CTRL+C` to stop following the log, then query the status API: ```bash curl -s http://127.0.0.1:9000/api/system/status ``` ```output {"id":"...","version":"25.1.0.102122","status":"UP"} ``` ## Step 6 - Publishing SonarQube with Nginx and HTTPS Install Nginx and Certbot with its Nginx plugin: ```bash sudo apt install nginx certbot python3-certbot-nginx ``` Create a server block for SonarQube: ```bash sudo nano /etc/nginx/sites-available/sonarqube ``` ```nginx server { listen 80; listen [::]:80; server_name sonar.your_domain; client_max_body_size 64m; 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; } } ``` Enable the site, test the syntax and reload Nginx: ```bash sudo ln -s /etc/nginx/sites-available/sonarqube /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` Allow HTTP and HTTPS through the firewall. If UFW is not active yet, allow SSH first so you do not lock yourself out: ```bash sudo ufw allow OpenSSH sudo ufw allow "Nginx Full" sudo ufw enable ``` Request a certificate. Certbot edits the server block to add TLS and an HTTP to HTTPS redirect: ```bash sudo certbot --nginx -d sonar.your_domain ``` ```output Successfully deployed certificate for sonar.your_domain to /etc/nginx/sites-enabled/sonarqube Congratulations! You have successfully enabled HTTPS on https://sonar.your_domain ``` Certbot also installs a systemd timer that renews the certificate automatically. You can test renewal with `sudo certbot renew --dry-run`. ## Step 7 - Securing the admin account Open `https://sonar.your_domain` in your browser. Log in with the default credentials `admin` / `admin`. SonarQube forces you to change the password on first login; choose a strong one. Then set the public URL so links in notifications and pull request decorations are correct: go to **Administration > Configuration > General Settings > General** and set **Server base URL** to `https://sonar.your_domain`. ## Step 8 - Analyzing your first project Create a project and a token for it: 1. Select **Create Project > Local project** (the wording can vary slightly between versions). 2. Enter a display name and a project key, for example `my-app`, and choose the main branch name. 3. Choose **Locally** as the analysis method and generate a project analysis token. Copy it; it is shown only once. In the root of your code repository, create a `sonar-project.properties` file: ```ini sonar.projectKey=my-app sonar.sources=src sonar.exclusions=**/node_modules/**,**/dist/** ``` The simplest way to run the scanner is the official Docker image, which bundles the right Java version, so you only need Docker on the machine that runs the analysis. From the repository root, run: ```bash docker run --rm \ -e SONAR_HOST_URL="https://sonar.your_domain" \ -e SONAR_TOKEN="your_project_token" \ -v "$(pwd):/usr/src" \ sonarsource/sonar-scanner-cli ``` At the end of the output the scanner prints a link to the results: ```output INFO ANALYSIS SUCCESSFUL, you can find the results at: https://sonar.your_domain/dashboard?id=my-app INFO EXECUTION SUCCESS ``` Open the link to see bugs, vulnerabilities, code smells and the quality gate status for the project. ## Step 9 - Running the analysis in GitLab CI To analyze every push and merge request, store the server URL and token as CI/CD variables in GitLab (**Settings > CI/CD > Variables**): `SONAR_HOST_URL` and `SONAR_TOKEN`, with the token marked as **Masked**. Then add this job to `.gitlab-ci.yml`: ```yaml 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.qualitygate.wait=true rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ``` `GIT_DEPTH: "0"` gives the scanner the full Git history, which it uses to detect new code and assign issues to authors. `sonar.qualitygate.wait=true` makes the job wait for the server's verdict and fail when the quality gate fails, so a merge request with new critical issues is blocked. The built-in **Sonar way** quality gate focuses on new code: it fails when new code has issues above the allowed rating, less than 80% coverage, more than 3% duplication or unreviewed security hotspots. You can copy it under **Quality Gates** and adjust the conditions to your team's standard. ## Step 10 - Backing up SonarQube All analysis data lives in PostgreSQL, and the configuration lives in `/opt/sonarqube/conf`. A consistent backup is a database dump plus a copy of that directory: ```bash sudo mkdir -p /var/backups/sonarqube sudo -u postgres pg_dump -Fc sonarqube | sudo tee /var/backups/sonarqube/sonarqube-$(date +%F).dump > /dev/null sudo tar -czf /var/backups/sonarqube/sonarqube-conf-$(date +%F).tar.gz -C /opt/sonarqube conf ``` You do not need to back up `/opt/sonarqube/data/es8` (or `es7` on older versions): the search index is rebuilt from the database if it is missing. ## Troubleshooting **SonarQube stops right after starting and `es.log` shows `max virtual memory areas vm.max_map_count [65530] is too low`.** The sysctl settings from Step 1 were not applied. Run `sudo sysctl --system` and check the value again. **`can not run elasticsearch as root`.** SonarQube was started as root, usually by running `sonar.sh` by hand. Stop it, run `sudo chown -R sonarqube:sonarqube /opt/sonarqube` to fix any files it created, and start it with `sudo systemctl start sonarqube`. **The service fails and `web.log` shows a JDBC or authentication error.** Check the `sonar.jdbc.*` lines in `sonar.properties` and test the login with the `psql -h 127.0.0.1` command from Step 2. **Each process logs to its own file.** Look in `/opt/sonarqube/logs/`: `sonar.log` (main process), `web.log` (web server), `ce.log` (background analysis) and `es.log` (Elasticsearch). ## Conclusion You have SonarQube Community Build running on Ubuntu 24.04 with PostgreSQL, managed by systemd and published over HTTPS through Nginx, and a GitLab CI job that blocks merge requests that fail the quality gate. Next, you can connect SonarQube to GitLab or GitHub for single sign-on, generate test coverage reports in your pipeline so SonarQube can track coverage, and schedule the backup commands from Step 10 with a systemd timer.