PostgreSQL en Kubernetes o gestionado: costes reales

CloudNativePG en Kubernetes o base PostgreSQL gestionada (OVHcloud, Scaleway, RDS): copias, PITR, costes mensuales y criterios de elección.

Tres servidores en torre alineados en una estantería de una sala de informática iluminada en azul

Respuesta corta: para una aplicación de pyme con una sola base de unas decenas de gigabytes, una base gestionada (OVHcloud, Scaleway, Amazon RDS) cuesta entre 25 y 120 € al mes y te libera de cualquier guardia sobre las copias de seguridad y las actualizaciones de versión. CloudNativePG en tu clúster de Kubernetes cuesta el precio de los nodos y del almacenamiento —a menudo 2 o 3 veces menos—, pero te hace responsable del PITR, de la conmutación y de las actualizaciones mayores. El criterio correcto no es el precio: es el número de personas capaces de restaurar la base a las 3 de la mañana.

Es la pregunta que surge sistemáticamente en cuanto un equipo termina de migrar su aplicación a Kubernetes. Los pods de la aplicación son stateless, el despliegue está limpio, la CI publica la imagen… y queda esa base PostgreSQL que sigue funcionando en una VM de 2021, con un pg_dump en un cron y nadie que haya probado la restauración en los últimos dieciocho meses.

Tres servidores en torre alineados en una estantería de una sala de informática iluminada en azul

Las tres opciones posibles, y la que hay que descartar

En realidad hay tres escenarios, no dos.

  1. PostgreSQL en el clúster, gestionado por un operador — CloudNativePG, Percona Operator, Zalando postgres-operator. El operador se encarga de la replicación, la conmutación, las copias de seguridad continuas y las actualizaciones menores.
  2. Base gestionada en un proveedor — OVHcloud Databases for PostgreSQL, Scaleway Database for PostgreSQL, Amazon RDS / Aurora, Neon o Supabase para las variantes serverless.
  3. PostgreSQL en el clúster sin operador — un StatefulSet escrito a mano o, peor aún, el chart de Helm de Bitnami dejado en replicaCount: 1 con las copias de seguridad «para hacer más adelante».

El escenario 3 hay que descartarlo. No porque PostgreSQL funcione mal en un contenedor —funciona muy bien—, sino porque todo lo que da valor a una base de datos en producción (archivado de WAL, promoción de una réplica, restauración a un instante determinado) es precisamente lo que un StatefulSet desnudo no hace. Y eso se descubre el día del incidente.

Regla de campo: si nadie del equipo sabe responder a «¿cuál es nuestro RPO y cuándo lo verificamos por última vez?», el problema no es la elección del alojamiento, es la ausencia de un procedimiento de restauración.

CloudNativePG: lo que el operador hace por ti

CloudNativePG es el operador de PostgreSQL desarrollado originalmente por EDB, aceptado como proyecto sandbox de la CNCF en enero de 2025 y promovido a incubating en 2026. Hoy es la referencia en el terreno nativo de Kubernetes: sin Patroni, sin etcd externo; la elección del primario se apoya en la propia API de Kubernetes.

La instalación cabe en un comando (comprueba el número de versión actual en el repositorio antes de ejecutarlo):

kubectl apply --server-side -f \
  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.27/releases/cnpg-1.27.0.yaml
kubectl get deploy -n cnpg-system cnpg-controller-manager

Un clúster de tres instancias con archivado continuo hacia un bucket compatible con S3 (aquí Scaleway Object Storage; el equivalente de OVHcloud funciona igual) se describe así:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: app-db
  namespace: production
spec:
  instances: 3
  imageName: ghcr.io/cloudnative-pg/postgresql:17.6
  storage:
    size: 50Gi
    storageClass: scw-bssd
  walStorage:
    size: 10Gi
  postgresql:
    parameters:
      shared_buffers: 512MB
      max_connections: "200"
  resources:
    requests:
      cpu: "1"
      memory: 2Gi
    limits:
      memory: 2Gi
  backup:
    retentionPolicy: "30d"
    barmanObjectStore:
      destinationPath: s3://mon-bucket-backups/app-db
      endpointURL: https://s3.fr-par.scw.cloud
      s3Credentials:
        accessKeyId:
          name: backup-creds
          key: ACCESS_KEY_ID
        secretAccessKey:
          name: backup-creds
          key: ACCESS_SECRET_KEY
      wal:
        compression: gzip

Dos puntos merecen destacarse.

walStorage separado. Poner los WAL en un volumen distinto al del directorio de datos evita que una saturación del archivado llene el volumen principal y bloquee las escrituras. Es la avería más frecuente en una base autoalojada.

barmanObjectStore = PITR. El archivado continuo de los WAL hacia el object storage es lo que permite la restauración a un instante determinado. Sin él tienes copias de base, es decir un RPO igual al intervalo entre dos copias —normalmente 24 h.

Restaurar a un instante determinado

La restauración no modifica el clúster existente: crea un nuevo clúster a partir del archivo. Es más seguro y se puede probar en staging sin riesgo.

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: app-db-restore
spec:
  instances: 1
  storage:
    size: 50Gi
  bootstrap:
    recovery:
      source: app-db-archive
      recoveryTarget:
        targetTime: "2026-11-06 14:25:00.00000+01"
  externalClusters:
    - name: app-db-archive
      barmanObjectStore:
        destinationPath: s3://mon-bucket-backups/app-db
        endpointURL: https://s3.fr-par.scw.cloud
        s3Credentials:
          accessKeyId:
            name: backup-creds
            key: ACCESS_KEY_ID
          secretAccessKey:
            name: backup-creds
            key: ACCESS_SECRET_KEY
        wal:
          maxParallel: 8

Pon este ejercicio en el calendario una vez por trimestre. Una restauración que nunca se ha ejecutado no es una copia de seguridad, es una intención.

Las actualizaciones mayores siguen siendo tu problema

CloudNativePG gestiona las actualizaciones menores (17.5 → 17.6) mediante un reinicio progresivo: primero las réplicas, luego una conmutación del primario, con un corte de unos segundos. Pasar de PostgreSQL 17 a 18 es otra historia: desde la versión 1.26, el operador sabe lanzar un pg_upgrade declarativo, pero eso implica una interrupción del servicio y un plan de marcha atrás. Ten en cuenta el calendario de soporte de PostgreSQL: cada versión mayor se mantiene cinco años, lo que te obliga a una migración mayor cada tres o cuatro años como máximo.

Mujer trabajando en un portátil frente a racks de servidores en una sala de informática en penumbra

Lo que realmente compra una base gestionada

Un servicio gestionado no vende potencia de cálculo, vende tiempo de guardia evitado. En concreto, esto es lo que incluyen los tres proveedores que más se encuentran en el mercado francés:

OVHcloud DatabasesScaleway DatabaseAmazon RDS
Alta disponibilidadnodos réplica opcionales según el planopción HA (nodo secundario)Multi-AZ opcional
Copias automáticasincluidas, retención según el planincluidas, retención configurableincluidas, de 1 a 35 días
PITRsísísí
Actualización de versión mayorasistida por el proveedorasistidapg_upgrade lanzado desde la consola
Acceso de superusuariononono
Extensioneslista blancalista blancalista blanca

Las tres limitaciones de la última fila son las verdaderas restricciones, mucho antes que el precio. Sin SUPERUSER, por tanto sin extensiones fuera de la lista blanca. Si tu aplicación necesita pg_cron, timescaledb o una extensión propia, compruébalo antes de firmar: es la causa número uno de vuelta atrás hacia el autoalojamiento.

Segundo punto a menudo descuidado: la latencia de red. Una base gestionada en la misma región que tu clúster añade normalmente un viaje de ida y vuelta por consulta. Para una API que hace 40 consultas SQL por página —algo que ocurre rápido con un ORM mal configurado— ese detalle se vuelve visible. Mide con pg_stat_statements antes de concluir que la lenta es la base.

La factura, partida por partida

Las tarifas evolucionan; los órdenes de magnitud, mucho menos. Comprueba siempre las páginas públicas de OVHcloud, Scaleway y AWS en el momento de decidir, pero razona con esta estructura.

Opción gestionada, base de 50 GB, 2 vCPU / 8 GB de RAM, con nodo secundario:

  • la instancia primaria y su réplica: la partida principal, facturada por horas;
  • el almacenamiento, facturado por gigabyte-mes;
  • las copias de seguridad más allá de la retención incluida;
  • el tráfico de salida si la base no está en la misma región que las aplicaciones.

Con esta configuración, en la práctica nos situamos en una horquilla de 60 a 150 € al mes en OVHcloud o Scaleway, y bastante más en AWS con Multi-AZ, al facturarse por separado el almacenamiento y las IOPS aprovisionadas.

Opción CloudNativePG, misma configuración, tres instancias:

  • ninguna licencia: el operador está bajo Apache 2.0;
  • la parte de CPU/RAM consumida en tus nodos existentes: si tu clúster ya tiene margen, el coste marginal es bajo; si no, cuenta con un nodo adicional;
  • tres volúmenes de datos persistentes más tres volúmenes WAL, facturados por gigabyte-mes por el proveedor de block storage;
  • el object storage para las copias de seguridad: unos pocos euros al mes para unos cientos de gigabytes de WAL comprimidos.

Se suele llegar a entre 25 y 60 € al mes de costes de infraestructura. La diferencia aparente es clara, pero omite deliberadamente la partida más cara.

La partida que nadie cuantifica

Pon frente al ahorro mensual el coste de la explotación: supervisión de las métricas (retraso de replicación, tasa de éxito del archivado de WAL, llenado de los volúmenes), prueba de restauración trimestral, migración mayor cada tres años y la disponibilidad de una persona competente en caso de incidente. A una tarifa diaria habitual de consultoría, dos jornadas de intervención anulan un año de ahorro con esta configuración.

La conclusión es, por tanto, menos económica que organizativa:

  • una sola persona domina PostgreSQL en el equipo → base gestionada, sin discusión;
  • varias bases, varios entornos, necesidad de extensiones no estándar → CloudNativePG, que resulta rentable a partir de tres o cuatro bases porque el operador se comparte;
  • un pico de carga estacional → la opción gestionada se redimensiona en unos minutos desde la consola, mientras que la autoalojada supone añadir un nodo al clúster.

Los errores que se ven con más frecuencia

El mismo proveedor para la base y las copias de seguridad. Enviar los WAL de una base de OVHcloud a un bucket de OVHcloud en la misma región equivale a aceptar que un incidente regional se lleve ambas cosas. Elige al menos otra región, idealmente otro proveedor, y activa la inmutabilidad de objetos cuando esté disponible.

Las copias de seguridad nunca restauradas. Ya se ha dicho, y sigue siendo la primera causa real de pérdida de datos.

Sin pool de conexiones. PostgreSQL asigna un proceso por conexión. Una aplicación Node.js que abre un pool de 20 conexiones por pod, multiplicado por 10 pods, satura un max_connections por defecto. CloudNativePG expone un Pooler basado en PgBouncer; en la opción gestionada, comprueba si se incluye un pooler y, si no, despliega uno en el clúster.

Sin un límite de memoria coherente. Si shared_buffers más el work_mem acumulado superan el límite de memoria del pod, el kernel mata el proceso (OOMKill) y asistes a una conmutación no deseada. Deja un margen de al menos el 25 % entre los parámetros de PostgreSQL y el límite del contenedor.

El almacenamiento en red mal elegido. Un volumen block estándar basta para la mayoría de las cargas de una pyme, pero una base transaccional sobre un volumen con pocas IOPS se traduce en tiempos de respuesta erráticos. Mira la clase de almacenamiento de tu storageClass antes de culpar a las consultas.

Si abordas este tema al mismo tiempo que el resto del clúster, los dos frentes vecinos son el punto de entrada HTTP —véase nuestro artículo sobre la migración de ingress-nginx a la Gateway API— y la autenticación en el registro de imágenes, detallada en las imágenes de GitLab privadas en Kubernetes. Nuestro equipo acompaña estas decisiones en el marco de nuestros servicios de infraestructura y explotación, en conexión con el desarrollo de aplicaciones cuando también hay que revisar el esquema de datos.

Preguntas frecuentes

¿PostgreSQL en un contenedor es algo serio en producción?

Sí, siempre que se use un operador. El contenedor no es el problema: lo son el almacenamiento persistente y la orquestación de la conmutación. CloudNativePG resuelve ambos apoyándose en los volúmenes persistentes y en la API de Kubernetes. Así funcionan en producción bases de varios terabytes. Un StatefulSet escrito a mano, en cambio, no es serio.

¿Qué diferencia hay entre RPO y RTO, en concreto?

El RPO es la cantidad de datos que aceptas perder: con un archivado continuo de WAL se cuenta en segundos; con un pg_dump nocturno, en horas. El RTO es el tiempo de restablecimiento del servicio: la promoción de una réplica tarda decenas de segundos, la reconstrucción de una base de 500 GB desde el object storage tarda decenas de minutos. Fija ambas cifras por escrito antes de elegir una arquitectura.

¿Se puede migrar de una base gestionada a CloudNativePG sin corte?

Casi. CloudNativePG sabe inicializar un clúster desde una base externa mediante replicación lógica (bootstrap.initdb.import o una suscripción a una publicación). Se replica en continuo, se verifica y luego se conmuta la aplicación durante una ventana de unos minutos. La restricción: la replicación lógica no transporta las secuencias —acuérdate de resincronizarlas— y exige PostgreSQL 10 o superior en ambos lados.

¿Hacen falta tres instancias o bastan dos?

Tres. Con dos instancias, la pérdida de la réplica te deja sin redundancia durante la reconstrucción, y el mantenimiento de un nodo se convierte en un punto de parada. Tres instancias permiten vaciar un nodo (kubectl drain) sin bajar nunca de una réplica síncrona disponible.

¿Y las ofertas serverless como Neon?

Son pertinentes para entornos de preproducción efímeros —el branching de base por rama de Git es una ventaja real— y para cargas muy irregulares. Para una aplicación de producción con tráfico regular, la facturación por uso rara vez resulta ventajosa, y la latencia de reactivación tras la suspensión no es aceptable en una página pública.

En resumen

  • Nunca un StatefulSet de PostgreSQL escrito a mano: o un operador, o una base gestionada.
  • CloudNativePG aporta replicación, conmutación, archivado de WAL y PITR declarativos, con licencia Apache 2.0, por el solo coste de los nodos y del almacenamiento: a menudo 25 a 60 €/mes en una configuración de pyme.
  • Una base gestionada (OVHcloud, Scaleway, RDS) cuesta más bien 60 a 150 €/mes con la misma configuración, pero elimina la guardia; comprueba la lista blanca de extensiones antes de comprometerte.
  • El criterio decisivo es humano: dos jornadas de intervención anulan un año de ahorro.
  • Separa los WAL del volumen de datos y envía las copias de seguridad a otro proveedor o, como mínimo, a otra región.
  • Prueba la restauración cada trimestre y deja por escrito tu RPO y tu RTO.
  • Pool de conexiones obligatorio (PgBouncer o el objeto Pooler de CloudNativePG) en cuanto varios pods de la aplicación se conecten.

¿Tienes dudas sobre la configuración adecuada o sobre la viabilidad de una migración sin corte? Escríbenos indicando el tamaño de tu base y tus requisitos de disponibilidad.