- Status: Accepted
- Date: 2026-04-29
- Deciders: OpenFoundry platform architecture group
- Related work:
docs/architecture/runtime-topology.md— "Postgres for service-owned relational state" (database-per-service).services/*/migrations/— cada servicio mantiene su propio esquema (p. ej.services/cipher-service/migrations,services/data-asset-catalog-service/migrations,services/marketplace-service/migrations,services/sql-warehousing-service/migrations,services/identity-federation-service/migrations, etc.).infra/k8s/cnpg/templates/cluster.yaml— plantilla de referencia del CRDpostgresql.cnpg.io/v1 Clusterusada por los servicios de plataforma.infra/k8s/rook/(cluster.yaml,objectstore.yaml,bucket.yaml) — Ceph + RGW como proveedor S3 cluster-local.- ADR-0007 (
docs/architecture/adr/ADR-0007-search-engine-choice.md) — precedente de "un único operador / un único stack stateful por capacidad".
OpenFoundry sigue el patrón base de datos por servicio: cada bounded
context posee su propio Postgres lógico, con migraciones versionadas dentro
de services/<svc>/migrations/. Esto se confirma en
docs/architecture/runtime-topology.md:
"Postgres for service-owned relational state […] The CI smoke job creates multiple service-specific databases, which strongly suggests database-per-service isolation rather than a shared operational schema."
Hoy conviven dos realidades:
- Un uso aislado de CloudNativePG ya introducido como plantilla de
referencia para clústeres Postgres de plataforma (ver
infra/k8s/cnpg/templates/cluster.yaml), donde el operador reconcilia un clúster con replicación en streaming y expone los servicios<name>-rw/<name>-ro. - El resto de servicios no tiene un operador estandarizado. Las
alternativas históricamente consideradas (Patroni externo + HAProxy/VIP,
StatefulSets "a mano", Postgres gestionado por proveedor) introducen:
- VIPs y balanceadores adicionales (SPOFs operativos),
- rutas de failover divergentes por servicio,
- backup/restore artesanal por equipo,
- dificultad para auditar RPO/RTO de forma homogénea,
- duplicidad de runbooks.
Sin un operador único:
- Cada equipo reinventa HA, backups, WAL archive, rotación de credenciales y upgrades menores/mayores.
- No hay una forma estándar de declarar topología (primary + réplicas), ventanas de mantenimiento, o políticas de retención.
- El plano de almacenamiento S3 disponible en cluster
(
infra/k8s/rook/objectstore.yaml) está infrautilizado para WAL/base backups.
El proyecto mantiene una postura 100% OSS (Apache-2.0 / MIT / BSD); el operador elegido debe encajar en esa restricción y alinearse con el precedente de ADR-0007 ("un solo stack stateful por capacidad").
- Operador Apache-2.0, nativo Kubernetes, sin dependencia de etcd externo ni de Patroni.
- CRD
Clusterque declara topología (instancias, sync replicas), bootstrap, storage, recursos, scheduled backups, ybarmanObjectStorepara WAL/base backups a S3. - Failover gestionado por el operador vía la API de Kubernetes (sin VIP).
- Servicios
<name>-rw,<name>-ro,<name>-rgenerados automáticamente → los servicios de aplicación se conectan por DNS estable. - Ya en uso como plantilla de referencia bajo
infra/k8s/cnpg/(precedente vivo en el repo).
- Requiere etcd/Consul externo, HAProxy y VIP por clúster.
- Añade SPOFs operativos (VIP, balanceador), runbooks de failover manuales y un plano de control fuera de Kubernetes.
- Backups y WAL archive resueltos con scripts ad-hoc (pgBackRest/barman invocados a mano).
- OSS (MIT), maduro, basado en Patroni interno.
- Modelo de configuración (Spilo/Patroni) más opaco que CNPG y con superficie de ajuste mayor.
- Integración de backups vía WAL-G; correcta pero menos declarativa que
barmanObjectStorede CNPG.
- OSS (AGPL-3.0 para componentes core en algunas distribuciones).
- Rechazada por incompatibilidad con la postura OSS del proyecto (Apache-2.0 / MIT / BSD), análoga a la lógica aplicada en ADR-0007.
- Mantener Postgres por servicio sin operador, con HA y backups gestionados por cada equipo.
- Maximiza la deuda operativa y dispersa los runbooks; no resuelve el problema planteado.
Adoptamos la Opción A — CloudNativePG (CNPG) como operador único de PostgreSQL para todos los clústeres Postgres del plano de producción de OpenFoundry.
- Operador único. CNPG (Apache-2.0) es el único operador soportado para
Postgres en
infra/k8s/**. No se introducirán Patroni externos, VIPs, HAProxy de Postgres, ni operadores alternativos (Zalando, StackGres, etc.) mientras este ADR esté vigente. - Topología por bounded context. Cada servicio que requiera Postgres
declara su propio CR
postgresql.cnpg.io/v1 Clustercon la topología base 1 primary + 2 réplicas (al menos 1 síncrona) (spec.instances: 3,spec.minSyncReplicas: 1,spec.maxSyncReplicas: 1). Esto preserva el aislamiento database-per-service descrito endocs/architecture/runtime-topology.md. - Backups y WAL archive a Ceph RGW. Todos los clústeres configuran
spec.backup.barmanObjectStoreapuntando as3://openfoundry-pg-backups/<service>/<cluster>/, sirviendo Ceph RGW declarado eninfra/k8s/rook/objectstore.yamlyinfra/k8s/rook/bucket.yaml. Esto incluye:- WAL archiving continuo (
wal: { compression: gzip }). - Base backups programados (
ScheduledBackup) con cadencia diaria. - Retención mínima de 14 días, máxima de 30 días por defecto; overridable por servicio.
- Cifrado en reposo delegado al bucket de Ceph RGW.
- WAL archiving continuo (
- Conexiones desde servicios. Los servicios consumen los endpoints
generados por CNPG:
<cluster>-rwpara escrituras,<cluster>-ropara lecturas escalables. No se permiten conexiones directas a pods ni VIPs externos. - Credenciales. Se gestionan mediante
Secretde tipokubernetes.io/basic-auth, siguiendo el patrón establecido eninfra/k8s/cnpg/templates/cluster.yaml. - Upgrades. Las versiones mayores de Postgres se planifican vía el
flujo de upgrade in-place del operador o mediante
Clusterde réplica lógica + cutover, según se documente en el runbook. - Desarrollo local.
infra/docker-compose.ymlyinfra/docker-compose.dev.ymlsiguen usando contenedores Postgres estándar para DX. CNPG aplica solo al plano Kubernetes.
- Una sola superficie operativa para todos los Postgres del plano de producción: un operador, un CRD, un runbook, un dashboard.
- HA declarativa (1 primary + 2 réplicas con al menos 1 síncrona) y failover automático sin VIPs ni SPOFs externos.
- Backups y PITR uniformes sobre Ceph RGW, reutilizando el almacenamiento
S3 ya provisto por
infra/k8s/rook/. - Aislamiento por bounded context preservado: cada servicio mantiene su
propio
Clustery sus propias migraciones bajoservices/<svc>/migrations/. - Coherencia con ADR-0007: una sola tecnología por capacidad stateful, reduciendo carga cognitiva y dependencias.
- 100% OSS (Apache-2.0).
- Acoplamiento del plano de datos a Kubernetes y a la API de CNPG.
- Coste base de 3 instancias por servicio que requiera HA estricta; los
servicios no críticos pueden declarar
instances: 1con backups (sin HA), pero deben justificarlo en su ADR de servicio. - Dependencia operativa adicional sobre Ceph RGW para la cadena de backup/restore: una indisponibilidad del object store degrada el WAL archive (no el plano de escritura, gracias al buffer local).
- Curva de aprendizaje del CRD
Clusterpara equipos que solo conocen Postgres "plano".
- La plantilla CNPG bajo
infra/k8s/cnpg/templates/cluster.yamlse considera el patrón de referencia. Nuevos charts de servicio deben reutilizar la misma forma (CRCluster+Secretbasic-auth). - No existen Patroni externos ni VIPs de Postgres en
infra/k8s/**al momento de este ADR; no hay nada que retirar. - Cualquier roadmap, prompt o documento que mencione "Patroni", "HAProxy para Postgres" o "Postgres administrado externamente" debe enlazar a este ADR y reformularse en términos de CNPG.
Este ADR debe reabrirse si cualquiera de las siguientes condiciones se cumple:
- CNPG cambia su licencia, gobierno o cadencia de releases de forma que deje de ser compatible con la postura OSS del proyecto (precedente Qdrant / OpenSearch en ADR-0007).
- Un destino regulado obliga al uso de un Postgres gestionado por un proveedor concreto o de un operador certificado distinto.
- Un workload concreto demuestra, con benchmarks reproducibles bajo
benchmarks/, que CNPG no puede sostener el RPO/RTO o el throughput requerido para un bounded context dado. - Se decide consolidar Postgres en un clúster compartido multi-tenant,
lo que rompería el principio "database-per-service" y requeriría
reabrir tanto este ADR como
docs/architecture/runtime-topology.md.
docs/architecture/runtime-topology.md— modelo "Postgres por servicio".docs/architecture/adr/ADR-0007-search-engine-choice.md— precedente de "un solo stack stateful por capacidad" y de filtrado por licencia OSS.infra/k8s/cnpg/templates/cluster.yaml— patrón de CRCluster+Secretbasic-auth de referencia.infra/k8s/rook/objectstore.yaml,infra/k8s/rook/bucket.yaml— proveedor S3 paras3://openfoundry-pg-backups/.services/*/migrations/— esquemas por servicio (p. ej.services/cipher-service/migrations,services/data-asset-catalog-service/migrations).- CloudNativePG: https://cloudnative-pg.io/ (Apache-2.0).
- Barman /
barmanObjectStore: https://cloudnative-pg.io/documentation/current/backup_recovery/.