ADR-0016: Declarative Database List in the Ansible postgres Role
Status: Proposed
Date: 2026-06-04
Deciders: Thanasis Katsadas
Context
The Themelio platform runs a shared CloudNativePG (CNPG) PostgreSQL cluster in the databases Kubernetes namespace. The cluster currently hosts databases for GitLab and Waldur. The Ansible postgres role (devops/ansible/roles/postgres/) deploys this cluster and provisions each database.
The role currently defines all database configuration as flat, per-database Ansible variables: gitlab_db_user, gitlab_db_name, gitlab_db_resource_name, waldur_db_user, waldur_db_name, and so on. Each database also requires its own Jinja2 template for the CNPG Database CRD and a separate template for the Kubernetes Secret that holds credentials. The tasks file contains a dedicated task block for each database โ validation, secret creation, and resource application โ all named after specific databases.
The platform is expanding. The k8s-root-actor service requires its own PostgreSQL database on the shared cluster, and additional per-service databases are likely in the future. Under the current structure, adding any database requires changes in four separate places inside the role: flat variables in defaults/main.yml, two new templates, and three new task entries. This pattern does not scale and introduces inconsistency risk.
See also: DD-0016: Implementation
Decision Drivers
- Adding a new database should require only a single list entry in inventory, with no role-internal changes.
- The role should remain self-consistent: adding a database entry is the only action needed.
- Vault password management should remain per-environment without changing structure.
- The role must not become responsible for configuring applications to connect to their databases; that remains with each service's own role.
Decision
We will replace all flat per-database variables with a postgres_databases list defined in inventory. Each list entry describes one CNPG Database resource. The role iterates over this list to validate passwords, create Kubernetes secrets, configure CNPG managed roles, and apply Database CRDs. Vault passwords become a per-environment vault_postgres_passwords dict keyed by PostgreSQL username. The role's templates are consolidated from five database/secret-specific files to two generic ones.
The postgres role remains responsible only for CNPG cluster infrastructure โ the cluster resource, Database CRDs, and credentials in the databases namespace. Each service role is responsible for projecting or mounting those credentials in its own namespace and application configuration.
Consequences
Positive
- Adding a new database requires editing only
inventory/common/group_vars/all.yml(one list entry) and the relevant environment vault file (one new password key). No changes to the role itself. - The role is closed to extension: it does not need to be modified when Themelio adds new services with databases.
- Templates shrink from five files to two, and tasks shrink from seven per-database task blocks to three loops.
- CNPG managed roles are derived automatically from the list, eliminating the hardcoded user section in
cnpg-cluster.yml.j2.
Negative / Risks
- The
postgres_databaseslist schema must be documented clearly; a missing field causes a runtime error rather than a clear validation message. - Deduplication logic for secrets (by
secret_name) and CNPG roles (byuser) requires reviewers to understand Jinja2uniquefilter semantics. - Existing vault files must be migrated from two flat vars (
vault_gitlab_db_password,vault_waldur_db_password) to the newvault_postgres_passwordsdict before the refactored role runs. This is a one-time migration; the actual passwords stay the same.
Page source
Source snapshot ยท Revision f928834c8.