Skip to content

ADR 013 — SonarQube for code quality ​

Status ​

Accepted

Context ​

Atlas hosts the projects I build, and I want an objective quality gate over the code I write — bugs, code smells, security hotspots, duplication, and coverage trends — instead of relying on memory and ad-hoc review. The candidates were:

  1. SonarQube Community Build — self-hosted, the OSS edition of the de-facto standard for static analysis; web UI, quality gates, broad language coverage.
  2. SonarCloud — the SaaS version. Free only for public repositories; private analysis is paid.
  3. Lighter linters wired into CI only (golangci-lint, ESLint, Semgrep, etc.) — no server, no historical trend, results scattered across CI logs.
  4. Nothing — accept that quality is tracked informally.

SonarQube is the tool I already know, it keeps history so I can see whether a project is improving, and self-hosting it means private repositories cost nothing. The notable operational catch is that, as of chart version 2026.1.0, the SonarSource Helm chart no longer bundles a PostgreSQL database — SonarQube now requires an external one.

Decision ​

Run SonarQube Community Build via the official SonarSource Helm chart, with native SonarQube login (no Authentik SSO for now). Provide its PostgreSQL with the CloudNativePG operator — a small single-instance Cluster in the sonarqube namespace whose generated credentials SonarQube consumes via jdbcOverwrite.

Consequences ​

Positive ​

  • Self-hosted private analysis at no per-repo cost, unlike SonarCloud's free tier.
  • Historical trends and quality gates in a real UI, not just CI log output.
  • CloudNativePG is a clean, Kubernetes-native database story: backups, failover primitives, and credential generation are the operator's job, and it is reusable by any future service that needs Postgres.
  • vm.max_map_count is set at the node level via Talos rather than a privileged init container, so the sonarqube namespace stays under the baseline Pod Security Standard.
  • Native Prometheus metrics (/api/monitoring/metrics) are scraped via the chart's PodMonitor and surfaced in Grafana (ADR 011).

Negative ​

  • It is heavy. SonarQube embeds Elasticsearch; with a dedicated Postgres alongside it is one of the larger single workloads on the node (~2 GB RAM under load).
  • An external database is now mandatory, which is why CloudNativePG enters the stack. That is one more operator to keep current.
  • No SSO yet. Community Build does not include SAML/OIDC, so logins are SonarQube-local. Wiring Authentik would require the commercial editions or a third-party plugin — deferred.
  • Talos sysctl changes require re-applying machine config (tofu apply in bootstrap/talos), a step outside the normal GitOps reconcile loop.

Why not SonarCloud ​

SonarCloud removes all operational burden but charges for private projects, which is exactly the use case here. Self-hosting trades RAM and a database for zero marginal cost and full data ownership.

Why CloudNativePG over a bundled or Bitnami PostgreSQL ​

The chart's bundled database was removed upstream, and pinning an older chart to keep it would freeze SonarQube on an old version. A generic Bitnami postgresql release would work but carries the 2025 Bitnami image-distribution changes; CloudNativePG is purpose-built for running Postgres on Kubernetes and is worth adopting as the platform's standard database operator.