PostgreSQL sur Kubernetes ou managé : coûts réels

CloudNativePG sur Kubernetes ou base PostgreSQL managée (OVHcloud, Scaleway, RDS) : sauvegardes, PITR, coûts mensuels et critères de choix.

Trois serveurs en tour alignés sur une étagère dans une salle informatique éclairée en bleu

Réponse courte : pour une application de PME avec une seule base de quelques dizaines de gigaoctets, une base managée (OVHcloud, Scaleway, Amazon RDS) coûte entre 25 et 120 € par mois et vous dispense de toute astreinte sur les sauvegardes et les montées de version. CloudNativePG dans votre cluster Kubernetes coûte le prix des nœuds et du stockage — souvent 2 à 3 fois moins — mais vous rend responsable du PITR, du basculement et des mises à jour majeures. Le bon critère n'est pas le prix : c'est le nombre de personnes capables de restaurer la base à 3 h du matin.

C'est la question qui revient systématiquement dès qu'une équipe a fini de migrer son application sur Kubernetes. Les pods applicatifs sont stateless, le déploiement est propre, la CI pousse l'image — et il reste cette base PostgreSQL qui tourne encore sur une VM de 2021, avec un pg_dump dans un cron et personne qui a testé la restauration depuis dix-huit mois.

Trois serveurs en tour alignés sur une étagère dans une salle informatique éclairée en bleu

Les trois options possibles, et celle qu'il faut écarter

Il y a en réalité trois scénarios, pas deux.

  1. PostgreSQL dans le cluster, géré par un opérateur — CloudNativePG, Percona Operator, Zalando postgres-operator. L'opérateur gère la réplication, le basculement, les sauvegardes continues et les mises à jour mineures.
  2. Base managée chez un fournisseur — OVHcloud Databases for PostgreSQL, Scaleway Database for PostgreSQL, Amazon RDS / Aurora, Neon ou Supabase pour les variantes serverless.
  3. PostgreSQL dans le cluster sans opérateur — un StatefulSet écrit à la main, ou pire, le chart Helm Bitnami laissé en replicaCount: 1 avec les sauvegardes « à faire plus tard ».

Le scénario 3 est à écarter. Non pas parce que PostgreSQL fonctionne mal dans un conteneur — il fonctionne très bien — mais parce que tout ce qui fait la valeur d'une base de données en production (archivage WAL, promotion d'un réplica, restauration à un instant donné) est précisément ce qu'un StatefulSet nu ne fait pas. Et c'est ce qu'on découvre le jour d'un incident.

Règle de terrain : si personne dans l'équipe ne sait répondre à « quel est notre RPO, et quand l'a-t-on vérifié pour la dernière fois ? », le sujet n'est pas le choix de l'hébergement, c'est l'absence de procédure de restauration.

CloudNativePG : ce que l'opérateur fait pour vous

CloudNativePG est l'opérateur PostgreSQL développé à l'origine par EDB, accepté comme projet sandbox de la CNCF en janvier 2025 puis passé en incubating en 2026. C'est aujourd'hui la référence côté Kubernetes natif : pas de Patroni, pas d'etcd externe, l'élection du primaire s'appuie sur l'API Kubernetes elle-même.

L'installation tient en une commande (vérifiez le numéro de version courante sur le dépôt avant d'exécuter) :

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 cluster à trois instances avec archivage continu vers un bucket S3 compatible (Scaleway Object Storage ici, l'équivalent OVHcloud fonctionne pareil) se décrit ainsi :

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

Deux points méritent d'être soulignés.

walStorage séparé. Mettre les WAL sur un volume distinct du répertoire de données évite qu'une saturation d'archivage ne remplisse le volume principal et ne bloque les écritures. C'est la panne la plus fréquente sur une base auto-hébergée.

barmanObjectStore = PITR. L'archivage continu des WAL vers l'object storage est ce qui permet la restauration à un instant donné. Sans lui, vous avez des sauvegardes de base, donc un RPO égal à l'intervalle entre deux sauvegardes — typiquement 24 h.

Restaurer à un instant donné

La restauration ne modifie pas le cluster existant : elle crée un nouveau cluster à partir de l'archive. C'est plus sûr, et c'est testable en staging sans risque.

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

Mettez cet exercice dans votre calendrier une fois par trimestre. Une restauration jamais jouée n'est pas une sauvegarde, c'est une intention.

Les mises à jour majeures restent votre problème

CloudNativePG gère les mises à jour mineures (17.5 → 17.6) par un redémarrage roulant : les réplicas d'abord, puis une bascule du primaire, avec une coupure de quelques secondes. Le passage de PostgreSQL 17 à 18 est une autre affaire : depuis la version 1.26, l'opérateur sait déclencher un pg_upgrade déclaratif, mais cela implique une interruption de service et un plan de retour arrière. Tenez compte du calendrier de support de PostgreSQL : chaque version majeure est maintenue cinq ans, ce qui vous impose une migration majeure tous les trois à quatre ans au plus tard.

Femme travaillant sur un ordinateur portable devant des baies de serveurs dans une salle informatique sombre

Ce que la base managée achète vraiment

Un service managé ne vend pas de la puissance de calcul, il vend du temps d'astreinte évité. Concrètement, ce qui est inclus chez les trois fournisseurs qu'on rencontre le plus souvent côté France :

OVHcloud DatabasesScaleway DatabaseAmazon RDS
Haute disponibiliténœuds réplicas en option selon le planoption HA (nœud secondaire)Multi-AZ en option
Sauvegardes autoincluses, rétention selon le planincluses, rétention configurableincluses, 1 à 35 jours
PITRouiouioui
Montée de version majeureassistée par le fournisseurassistéepg_upgrade déclenché depuis la console
Accès superutilisateurnonnonnon
Extensionsliste blancheliste blancheliste blanche

Les trois limites de la dernière ligne sont les vraies contraintes, bien avant le prix. Pas de SUPERUSER, donc pas d'extension hors liste blanche. Si votre application a besoin de pg_cron, de timescaledb ou d'une extension maison, vérifiez-le avant de signer : c'est la cause numéro un de retour en arrière vers l'auto-hébergement.

Deuxième point souvent négligé : la latence réseau. Une base managée dans la même région que votre cluster ajoute typiquement un aller-retour réseau par requête. Pour une API qui fait 40 requêtes SQL par page — ce qui arrive vite avec un ORM mal configuré — ce détail devient visible. Mesurez avec pg_stat_statements avant de conclure que c'est la base qui est lente.

La facture, poste par poste

Les grilles tarifaires évoluent ; les ordres de grandeur, beaucoup moins. Vérifiez toujours sur les pages publiques d'OVHcloud, Scaleway et AWS au moment de votre arbitrage, mais raisonnez sur cette structure.

Option managée, base de 50 Go, 2 vCPU / 8 Go de RAM, avec nœud secondaire :

  • l'instance primaire et son réplica : le poste principal, facturé à l'heure ;
  • le stockage, facturé au gigaoctet-mois ;
  • les sauvegardes au-delà de la rétention incluse ;
  • le trafic sortant si la base n'est pas dans la même région que les applications.

Sur ce gabarit, on se situe en pratique dans une fourchette de 60 à 150 € par mois chez OVHcloud ou Scaleway, et sensiblement plus chez AWS en Multi-AZ, le stockage et les IOPS provisionnées étant facturés séparément.

Option CloudNativePG, même gabarit, trois instances :

  • aucune licence : l'opérateur est en Apache 2.0 ;
  • la part CPU/RAM consommée sur vos nœuds existants — si votre cluster a déjà de la marge, le coût marginal est faible, sinon comptez un nœud supplémentaire ;
  • trois volumes de données persistants plus trois volumes WAL, facturés au gigaoctet-mois par le fournisseur de block storage ;
  • l'object storage pour les sauvegardes : quelques euros par mois pour quelques centaines de gigaoctets de WAL compressés.

On arrive souvent entre 25 et 60 € par mois de coûts d'infrastructure. L'écart apparent est net — mais il omet volontairement le poste le plus cher.

Le poste que personne ne chiffre

Mettez en face du gain mensuel le coût de l'exploitation : surveillance des métriques (lag de réplication, taux de réussite de l'archivage WAL, remplissage des volumes), test de restauration trimestriel, migration majeure tous les trois ans, et la disponibilité d'une personne compétente en cas d'incident. À un TJM de prestation courant en Île-de-France, deux journées d'intervention annulent une année d'économies sur ce gabarit.

La conclusion est donc moins économique qu'organisationnelle :

  • une seule personne maîtrise PostgreSQL dans l'équipe → base managée, sans discussion ;
  • plusieurs bases, plusieurs environnements, un besoin d'extensions non standard → CloudNativePG, qui devient rentable dès trois ou quatre bases parce que l'opérateur se mutualise ;
  • un pic de charge saisonnier → le managé se redimensionne en quelques minutes depuis la console, là où l'auto-hébergé suppose d'ajouter un nœud au cluster.

Les erreurs qu'on voit le plus souvent

Le même fournisseur pour la base et les sauvegardes. Envoyer les WAL d'une base OVHcloud vers un bucket OVHcloud dans la même région, c'est accepter qu'un incident de région emporte les deux. Choisissez au minimum une autre région, idéalement un autre fournisseur, et activez l'immuabilité objet quand elle est disponible.

Les sauvegardes jamais restaurées. Déjà dit, et ça reste la première cause de perte de données réelle.

Pas de pool de connexions. PostgreSQL alloue un processus par connexion. Une application Node.js qui ouvre un pool de 20 connexions par pod, multiplié par 10 pods, sature un max_connections par défaut. CloudNativePG expose un Pooler basé sur PgBouncer ; côté managé, vérifiez si un pooler est fourni, sinon déployez-en un dans le cluster.

Pas de limite mémoire cohérente. Si shared_buffers plus le work_mem cumulé dépassent la limite mémoire du pod, le noyau tue le processus (OOMKill) et vous assistez à un basculement non désiré. Gardez une marge d'au moins 25 % entre les paramètres PostgreSQL et la limite du conteneur.

Le stockage réseau mal choisi. Un volume block standard suffit pour la plupart des charges d'une PME, mais une base transactionnelle sur un volume à faibles IOPS se traduit par des temps de réponse erratiques. Regardez la classe de stockage de votre storageClass avant d'incriminer les requêtes.

Si vous vous attaquez à ce sujet en même temps qu'au reste du cluster, les deux chantiers voisins sont le point d'entrée HTTP — voir notre article sur la migration d'ingress-nginx vers la Gateway API — et l'authentification au registre d'images, détaillée dans les images GitLab privées sur Kubernetes. Notre équipe accompagne ces arbitrages dans le cadre de nos prestations d'infrastructure et d'exploitation, en lien avec le développement applicatif quand le schéma de données est aussi à revoir.

Questions fréquentes

PostgreSQL dans un conteneur, est-ce sérieux en production ?

Oui, à condition d'utiliser un opérateur. Le conteneur n'est pas le problème : le stockage persistant et l'orchestration du basculement le sont. CloudNativePG résout les deux en s'appuyant sur les volumes persistants et l'API Kubernetes. Des bases de plusieurs téraoctets tournent ainsi en production. Un StatefulSet écrit à la main, en revanche, n'est pas sérieux.

Quelle différence entre RPO et RTO, concrètement ?

Le RPO est la quantité de données que vous acceptez de perdre : avec un archivage WAL continu, il se compte en secondes ; avec un pg_dump nocturne, en heures. Le RTO est le temps de remise en service : une promotion de réplica prend des dizaines de secondes, la reconstruction d'une base de 500 Go depuis l'object storage prend des dizaines de minutes. Fixez les deux chiffres par écrit avant de choisir une architecture.

Peut-on migrer d'une base managée vers CloudNativePG sans coupure ?

Presque. CloudNativePG sait amorcer un cluster depuis une base externe par réplication logique (bootstrap.initdb.import ou un abonnement à une publication). On réplique en continu, on vérifie, puis on bascule l'application pendant une fenêtre de quelques minutes. La contrainte : la réplication logique ne transporte pas les séquences — pensez à les resynchroniser — et exige PostgreSQL 10 ou supérieur des deux côtés.

Faut-il trois instances ou deux suffisent ?

Trois. Avec deux instances, la perte du réplica vous laisse sans redondance pendant la reconstruction, et une maintenance de nœud devient un point d'arrêt. Trois instances permettent de vidanger un nœud (kubectl drain) sans jamais descendre sous un réplica synchrone disponible.

Et les offres serverless comme Neon ?

Elles sont pertinentes pour des environnements de préproduction éphémères — le branchement de base par branche Git est un vrai gain — et pour des charges très irrégulières. Pour une application de production à trafic régulier, la facturation à l'usage devient rarement avantageuse, et la latence de réveil après mise en veille n'est pas acceptable sur une page publique.

En résumé

  • Jamais de StatefulSet PostgreSQL écrit à la main : soit un opérateur, soit du managé.
  • CloudNativePG apporte réplication, basculement, archivage WAL et PITR déclaratifs, sous licence Apache 2.0, pour le seul coût des nœuds et du stockage — souvent 25 à 60 €/mois sur un gabarit de PME.
  • Une base managée (OVHcloud, Scaleway, RDS) coûte plutôt 60 à 150 €/mois sur le même gabarit mais supprime l'astreinte ; vérifiez la liste blanche d'extensions avant de vous engager.
  • Le critère décisif est humain : deux journées d'intervention annulent une année d'économies.
  • Séparez les WAL du volume de données et envoyez les sauvegardes chez un autre fournisseur ou au minimum dans une autre région.
  • Testez la restauration chaque trimestre et écrivez noir sur blanc votre RPO et votre RTO.
  • Pool de connexions obligatoire (PgBouncer, ou l'objet Pooler de CloudNativePG) dès que plusieurs pods applicatifs se connectent.

Un doute sur le gabarit à retenir ou sur la faisabilité d'une migration sans coupure ? Écrivez-nous avec la taille de votre base et vos contraintes de disponibilité.