ADR-009 — Dos bases de datos PostgreSQL separadas¶
| Campo | Valor |
|---|---|
| Estado | Aceptado |
| Fecha | 11 de febrero de 2026 |
| Decisores | Equipo Custodiam (Marcos Val Sanz, Rodrigo Mulero García) |
Contexto¶
El stack del proyecto incluye dos servicios que necesitan persistencia relacional:
custodiam-api— backend FastAPI con SQLModel + Alembic (ADR-002, ADR-003) — gestiona las entidades de negocio (voluntarios, servicios, acreditaciones, fichajes, inventario, etc.).- Keycloak — Identity Provider que gestiona el realm
custodiam, usuarios, roles, sesiones, tokens emitidos. Internamente Keycloak crea ~70 tablas propias para su modelo (realms, clients, users, credentials, federated_identity, refresh tokens, sessions, etc.).
Una sola instancia de PostgreSQL puede alojar las dos cargas. Hay que decidir si las dos comparten una sola base de datos lógica o si cada una vive en su propia base de datos lógica dentro del mismo cluster.
Decisión¶
Dos bases de datos lógicas separadas dentro del mismo contenedor postgres:
postgres (contenedor)
├── BD: custodiam ← gestionada por custodiam-api + Alembic
└── BD: custodiam_kc ← gestionada internamente por Keycloak
El init-script init-db.sh montado en /docker-entrypoint-initdb.d/ del contenedor crea ambas bases al primer arranque del volumen. Las cadenas de conexión son distintas:
- API:
postgresql+psycopg://custodiam:<password>@postgres:5432/custodiam - Keycloak:
jdbc:postgresql://postgres:5432/custodiam_kc
Ambas bases comparten el cluster (mismo proceso, mismo volumen postgres_data, mismo backup), pero son independientes a nivel de schema, tablas y usuarios SQL.
Justificación¶
-
Elimina el conflicto de Alembic
--autogenerate. Si la API y Keycloak compartieran base de datos,alembic revision --autogeneratedetectaría las ~70 tablas de Keycloak como "tablas no declaradas en el modelo Python" e intentaría incluirlas en la migración comoDROP TABLE. Cualquier desarrollador que no se diese cuenta y aplicara la migración conalembic upgrade headdestruiría el realm completo de Keycloak. Separar las bases de datos hace que--autogeneratesolo vea las tablas de la API. -
Separación de responsabilidades. El esquema de Keycloak es opaco para el equipo: cambia con cada versión mayor de Keycloak, no se documenta a nivel de tablas, y solo Keycloak mismo debe operarlo. Ponerlo junto a las tablas de negocio mezcla dos modelos mentales distintos en un solo namespace SQL.
-
Backup y restore granulares. Es perfectamente posible hacer
pg_dumpsolo de la basecustodiam(datos de negocio) sin arrastrar el estado de sesiones de Keycloak, que es información volátil y puede regenerarse. Esto facilita compartir snapshots de desarrollo / pruebas sin filtrar datos de sesión potencialmente sensibles. -
Diferentes patrones de mantenimiento. La base
custodiamevoluciona con migraciones Alembic versionadas en el repo. La basecustodiam_kcla migra el propio Keycloak en cada arranque con su versión mayor. Tener una sola base mezclaría dos lifecycles de schema management que el sistema no debe coordinar. -
Costo cero. PostgreSQL aloja N bases de datos en una sola instancia sin sobrecosto significativo de recursos. No se necesita un segundo contenedor de Postgres. La diferencia frente a "una sola BD compartida" es operativa, no de infraestructura.
Alternativas evaluadas y descartadas¶
A. Una sola base de datos compartida con prefijos en nombres de tablas¶
API en tablas app_voluntarios, app_servicios, etc.; Keycloak en sus tablas habituales sin prefijo.
- Pros: una sola conexión, una sola URL en
.env. - Contras: requiere reconfigurar todas las queries de la API para usar prefijos; no resuelve el problema fundamental de
--autogenerate(Alembic sigue detectando las tablas de Keycloak como ajenas); ensucia el namespace para una ganancia mínima. - Descartado por: no resuelve el conflicto principal.
B. Una sola base de datos compartida con schemas PostgreSQL separados¶
API en schema app.*, Keycloak en schema auth.*.
- Pros: separación lógica explícita, una sola BD.
- Contras: Keycloak no soporta correr en un schema distinto al
publicpor defecto (se puede forzar pero requiere configuración avanzada y se han reportado bugs); Alembic sigue necesitando configuración explícita para limitar--autogeneratea un schema. La complejidad operativa supera el beneficio. - Descartado por: complejidad innecesaria.
C. Dos contenedores PostgreSQL independientes¶
Uno para la API, otro para Keycloak.
- Pros: aislamiento absoluto.
- Contras: duplica recursos (dos procesos Postgres, dos volúmenes, dos pares de healthchecks); duplica la complejidad de backups y de monitorización; sin beneficio observable sobre dos BDs en el mismo cluster.
- Descartado por: sobreingeniería para el aislamiento que dos BDs lógicas ya proporcionan.
Implicaciones operativas¶
init-db.shmontado endocker-entrypoint-initdb.d/: el script crea las dos bases en el primer arranque del contenedor (cuando el volumenpostgres_dataestá vacío). En arranques posteriores no se ejecuta, así que ambas BDs persisten.- Credenciales separadas en el
.env.sops: dos pares user/password distintos (CUSTODIAM_DB_PASSWORDpara la API,KC_DB_PASSWORDpara Keycloak). Permite rotarlas independientemente. - Backups:
pg_dumpallcon el contenedor parado captura ambas bases. Para snapshots selectivos:pg_dump -U custodiam -d custodiam > custodiam.sqldesde fuera del contenedor. - Migración de versión mayor de Keycloak: Keycloak migra automáticamente sus tablas internas al arrancar con una versión nueva (es operación idempotente). No interfiere con
custodiam-apiporque las dos bases son independientes. docker-compose.ymldeclara ambas variables de entorno en el servicio Keycloak (KC_DB_URL=jdbc:postgresql://postgres:5432/custodiam_kc) y en el servicio API (DATABASE_URL=postgresql+psycopg://custodiam:...@postgres:5432/custodiam). Sin acoplamiento entre ellas.
Referencias¶
- PostgreSQL — Database Roles and Authentication — modelo de usuarios, roles y permisos sobre BDs lógicas separadas.
- Keycloak — Configuring the database — variables de entorno aceptadas (
KC_DB,KC_DB_URL, etc.). - ADR-002 SQLModel y ADR-003 Alembic — capa que toca la BD
custodiam. - ADR-008 psycopg3 — driver con el que la API conecta.