> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

> **Themelio compatibility reference.** This page describes the retained Themelio implementation or its historical design. Check the [parallel-operation guide](/compatibility/index.md) before applying it to the current platform.

# 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](/dd/0016-declarative-database-list-in-postgres-role.md)

## 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_databases` list 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 (by `user`) requires reviewers to understand Jinja2 `unique` filter semantics.
- Existing vault files must be migrated from two flat vars (`vault_gitlab_db_password`, `vault_waldur_db_password`) to the new `vault_postgres_passwords` dict before the refactored role runs. This is a one-time migration; the actual passwords stay the same.

<details class="page-source"><summary>Page source</summary>

<a href="/source/docs/content/adr/0016-declarative-database-list-in-postgres-role.md.txt">Source snapshot</a> · Revision `f928834c8`.

</details>
