Skip to main content
  1. Posts/

CloudNativePG vs RDS for PostgreSQL: Cost, Control, and Tradeoffs

Introduction
#

CloudNativePG has a reputation problem.

When people first hear about it, they often bucket it into the same mental category as “just self-host your database”. Another common concern I’ve heard is the outdated belief that “Kubernetes does not handle state well”. That framing misses what CloudNativePG actually is. CloudNativePG is an operator. It handles a large amount of the lifecycle work that teams usually associate with a managed database service: failover, rolling updates, replica management, service management, backups, recovery workflows, and day-2 operations.

CNPG being an operator makes it much closer to a provider-agnostic, open source alternative to RDS than many people assume.

Nowadays it’s rare if your company doesn’t already run Kubernetes for application compute. Running your databases inside the same Kubernetes cluster via CloudNativePG can be a very compelling way to lower PostgreSQL costs, and improving the software development lifecycle around it. Think about it, less Terraform and instead just have a Cluster manifest along side your application Deployment.

RDS PostgreSQL and RDS Aurora PostgreSQL-Compatible Edition Are Not the Same
#

This comparison is mostly about RDS for PostgreSQL, not Aurora.

That distinction matters because AWS sells multiple PostgreSQL-shaped products that solve different problems and use different storage models.

RDS for PostgreSQL is still a very managed product, but it is closer to the traditional shape of running PostgreSQL on provisioned infrastructure. AWS documents it as using EBS volumes for database and log storage. You still make explicit decisions about instance class, storage type, storage size, IOPS, and sometimes throughput. That is much closer to the set of decisions you make with CloudNativePG.

CloudNativePG is not trying to imitate Aurora. Although it can be deployed in more advanced ways to get more of the features that Aurora offers.

Aurora is the more different product, and usually the more expensive baseline.

In general Aurora is much more scalable out of the box. Aurora uses a shared cluster volume that automatically grows as data increases. In practice, that means AWS manages the storage layer very differently than standard RDS PostgreSQL. That is one of Aurora’s selling points, but it also means Aurora is not a clean apples-to-apples comparison for CloudNativePG.

Aurora has the same scalable pattern for the IO rate…the more you use the more AWS scales up, and the more you pay as a result.

CloudNativePG is not just PostgreSQL on a server
#

This is the most important point in the whole article.

CloudNativePG is not equivalent to installing PostgreSQL on a Linux VM and hoping some automation around it holds together. It is an operator-managed control plane for PostgreSQL on Kubernetes.

CloudNativePG manages:

  • primary and replica instances
  • service endpoints for read-write and read-only access
  • planned switchovers and automated failover
  • rolling updates for PostgreSQL images and related restarts
  • backup and recovery integrations
  • PVC lifecycle directly, without relying on StatefulSets
  • Kubernetes Secret management for databases (That your app can also use)

Where the operational overhead is lower than people think
#

There is still operational ownership with CloudNativePG. That part is real. But the overhead is often overstated.

What people imagine is:

  • manual failover playbooks
  • fragile homemade backup scripts
  • SSH access to pets
  • database upgrades that require custom rituals

That is not the model here.

CloudNativePG gives you a supported operational path for common production tasks. It also fits neatly into the tools many Kubernetes teams already use for the rest of their platform: manifests, GitOps, metrics, logging, storage classes, and object storage.

If your company already trusts Kubernetes to run revenue-generating applications, then the idea that PostgreSQL must be handled in a completely separate operational universe starts to break down.

A practical cost comparison
#

Here is the fairest way to think about cost.

Do not compare Multi-AZ RDS to a single PostgreSQL pod on Kubernetes. Compare a production-capable RDS deployment to a production-capable CloudNativePG cluster.

For this example, assume:

  • AWS us-east-2
  • RDS for PostgreSQL as a Multi-AZ DB cluster with two readable standbys, 1 writable.
  • CloudNativePG with 3 instances for high availability
  • the company already runs Kubernetes for application compute
  • backups are stored in object storage

A simple pricing example
#

For a rough comparison, assume a moderately small but still production-shaped PostgreSQL deployment:

  • 2 vCPU / 16 GiB class database nodes
  • 200 GiB of general-purpose SSD storage
  • high availability on both sides
  • 730 hours in a month

These are not exact calculator numbers. They are reasonable us-east-2 ballpark numbers meant to show the shape of the tradeoff.

Item RDS PostgreSQL Multi-AZ DB cluster CNPG on EC2
Compute about $985.50/month for 3 x db.r6g.large across three AZs about $220/month for 3 x r6g.large EC2 nodes worth of database compute
Storage about $138/month for 3 x 200 GiB gp3 rds storage volumes about $48/month for 3 x 200 GiB gp3 EBS volumes
Backups $0 since backups are included (unless you increase retention) about $5-15/month in S3-compatible object storage
Total about $1,123.50/month about $273-283/month

This doesn’t include features like RDS proxy. CNPG provides a Pooler resource that can server the same role RDS Proxy often serves in AWS, without adding a meaningful amount of extra cost.

Even with conservative assumptions, the CNPG side is roughly $840/month cheaper in this example.

What those numbers are really saying
#

The RDS total is mostly paying for:

  • the managed database control plane
  • three database instances across three AZs
  • block storage for each cluster member
  • lower platform ownership for your team

The CNPG total is mostly paying for:

  • the PostgreSQL compute you already know you need
  • block storage for each cluster member
  • object storage for backups and WAL archives

The important detail is that this CNPG estimate is already using a fairly conservative view of cost. It assumes you pay for the full compute footprint of three PostgreSQL instances on EC2.

In real companies, the marginal cost can be lower than that because Kubernetes capacity is often already present for application workloads. If the cluster already has headroom, the practical cost delta may feel even more favorable than the table suggests.

What I would tell a company looking at this
#

If you want the shortest path to “AWS handles more of this for me,” RDS is still valid. But if your company already runs Kubernetes competently, it is hard to ignore the economics.

Saving roughly $840 per month on one modest PostgreSQL deployment is not life-changing by itself. Saving that across several environments or several services starts to matter quickly. More importantly, you are reducing spend without dropping all the way down to “just run PostgreSQL on a server” operations.

That is the part many people underestimate about CloudNativePG.

The other benefit of CNPG is the software development lifecycle. You can now run your cluster infrastructure the same way you manage your apps inside Kubernetes. When you create a CNPG Cluster it also creates a secret with all the info your app will need to communicate with the psql instance. Imagine a Cluster manifest next to your Deployment manifest and that’s it, your app and database are up and ready to go.

Best practices I would use with CloudNativePG
#

If I were recommending CloudNativePG in production, I would not stop at “install the operator”. I would want a few operational defaults in place.

Prefer primaryUpdateMethod: switchover
#

For rolling updates, I prefer:

spec:
  primaryUpdateStrategy: unsupervised
  primaryUpdateMethod: switchover

CNPG supports both restart and switchover. I generally prefer switchover because it promotes an already-updated replica and makes the update path easier to reason about in production.

There is an important caveat from the docs: when using switchover, you cannot change the PostgreSQL image and PostgreSQL configuration parameters at the same time. Those changes need to happen sequentially.

Use Guaranteed QoS
#

The CNPG documentation explicitly recommends Guaranteed QoS for PostgreSQL workloads.

That means setting CPU and memory requests equal to limits:

spec:
  resources:
    requests:
      memory: "1024Mi"
      cpu: "1"
    limits:
      memory: "1024Mi"
      cpu: "1"

This is not just a Kubernetes style preference. It directly affects eviction behavior and helps keep the PostgreSQL instance alive more predictably under memory pressure.

I would also size PostgreSQL memory settings consistently with pod resources. CNPG’s docs call out shared_buffers as a good example, with 25% of available memory as a reasonable starting point.

Treat WAL archiving as a production default
#

CloudNativePG’s documentation recommends configuring a WAL archive in production. I agree.

Backups are not enough by themselves if your recovery expectations are real. A usable recovery story should include:

  • base backups
  • WAL archiving
  • recovery testing
  • a clear understanding of RPO and RTO

Use the Barman Cloud plugin deliberately
#

The Barman Cloud plugin is the modern direction for CNPG backup and recovery integration.

One detail that is easy to miss is the serverName behavior. The official CNPG major upgrade documentation recommends changing the serverName when performing major PostgreSQL upgrades so archives do not get mixed across versions. Their example uses names like cluster-example-pg16 and cluster-example-pg18.

That recommendation is worth adopting as a general convention. Including the PostgreSQL major version in serverName makes the archive layout easier to reason about and helps preserve clean recovery boundaries across upgrades.

With the plugin, that setting belongs in the plugin parameters on the Cluster, not in the ObjectStore itself.

spec:
  plugins:
    - name: barman-cloud.cloudnative-pg.io
      isWALArchiver: true
      parameters:
        barmanObjectName: cluster-example-backups
        serverName: cluster-example-pg16

Enable monitoring to Prometheus
#

With AWS RDS you can enable monitoring with a click of a button. Enable monitoring and all your database metrics are in CloudWatch.

If you have apps in a Kubernetes cluster you are probably collecting metrics for them using the Prometheus operator. To enable database monitoring for your CNPG databases you use the same method as your app.

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: app-psql-metrics
spec:
  selector:
    matchLabels:
      cnpg.io/cluster: app-psql
  podMetricsEndpoints:
    - port: metrics

Now your database metrics are in Prometheus and you can import or create Grafana dashboards just like you do with your applications.

When I would choose RDS instead
#

I would still choose RDS PostgreSQL when:

  • the team does not already run Kubernetes well
  • the company wants to minimize database platform ownership at almost any price
  • the operational maturity around backups, storage, and recovery is weak

There is nothing wrong with paying for managed services when that is the correct business choice.

When I would seriously consider CloudNativePG
#

I would seriously consider CloudNativePG when:

  • the company already uses Kubernetes for most application compute
  • the team wants more control over cost and platform shape
  • the organization wants a provider-neutral PostgreSQL option
  • engineering is willing to own a well-defined but not unreasonable slice of database operations

That is a very common profile now.

Conclusion
#

CloudNativePG is better than many people think.

It is not just PostgreSQL on Kubernetes. It is an operator-managed PostgreSQL platform that can cover much more of the day-2 operational surface area than people expect. For teams that already use Kubernetes as their main compute platform, the operational overhead is often lower than the critics assume and the cost savings can be real.

RDS PostgreSQL is still a good product. Aurora is still a good product. But neither should automatically win by default just because the database happens to be PostgreSQL.

If your company already runs Kubernetes competently, CloudNativePG deserves a much more serious look than it usually gets.