Managed Postgres is the default answer for most teams. But the pricing model of serverless databases (Neon, Supabase) and managed instances (Amazon RDS) is built around compute that is billed continuously, while a self-hosted PostgreSQL on a VPS is a fixed monthly cost that does not grow with your load.

At some point the curves cross. For a steady, always-on workload, a single VPS running PostgreSQL plus PgBouncer is often dramatically cheaper than a managed database of equivalent size. This is that comparison: the economics, the latency story, and a decision table to help you pick.

The version hook matters: PG14 is at end of life

Before the cost comparison, a note on versions, because it directly affects a self-hosted setup.

PostgreSQL supports each major version for five years after initial release.[1] PostgreSQL 18 is the current stable line (current minor 18.6),[1] released September 25, 2025.[1] PostgreSQL 14 was released September 30, 2021, and reaches its final supported minor on November 12, 2026.[1] After that date it receives no more bug or security fixes from the project,[1] so if you self-host, running 14 after November 2026 is a security liability, and managed providers will push you off it anyway.

The practical takeaway: if you build a new self-hosted setup in late 2026, start on PostgreSQL 16 or 18, both comfortably inside their support windows,[1] and plan a minor-release upgrade path.

How the pricing models actually differ

The core difference is billing granularity.

Neon charges per compute unit per hour. A Compute Unit (CU) is roughly 4 GB of RAM plus CPU and SSD,[2] and you pay for the time the compute runs: on the Launch plan, $0.106 per CU-hour, and on the Scale plan, $0.222 per CU-hour.[2] When compute suspends (scale-to-zero), you stop accruing CU-hours, so idle time costs nothing.[2] Storage is billed separately at $0.35 per GB-month,[2] and the included egress on paid plans is 500 GB per project, after which it is $0.10 per GB.[2]

Supabase bills a plan subscription plus compute per project. The Pro plan starts at $25 per month, which includes the platform quotas and $10 per month in compute credits that cover one project on the smallest “Micro” compute instance.[3] Additional or larger compute is billed per project on top of the plan.[3] A single database-only project on Micro compute works out to $25 per month before usage beyond the included quotas.[3] The Free tier gives you 500 MB of database and pauses projects after one week of inactivity; paid projects are not paused.[3]

Amazon RDS bills on-demand database instances by the hour, in addition to storage and I/O.[4] You pay from the moment an instance runs, with a 10-minute minimum per billing change,[4] and Multi-AZ standby instances double the compute cost because a standby runs continuously.[4] RDS does offer a free tier with 750 hours on Single-AZ instances for the first year,[4] which makes it cheap to start but expensive once you outgrow it.

A VPS is a flat monthly price. DigitalOcean Droplets start at $4 per month,[6] and Hetzner Cloud is one of the cheapest options in Europe: Cost-Optimized plans start at €5.99 per month (excl. VAT) and Regular Performance at €6.49 per month.[5][7] A 1 vCPU / 2 GB box in that range runs PostgreSQL and PgBouncer comfortably for a small-to-medium application, and the price does not change whether it is idle or busy.

So the managed providers optimize for elastic, spiky workloads where you pay for what you use and can scale to zero. The VPS optimizes for a steady workload: one predictable bill.

The cold-start and latency tradeoff

Serverless Postgres pricing depends on suspending compute when idle, and suspension introduces cold starts. Neon’s compute suspends after five minutes of inactivity by default.[2] The first query after a wake-up has to spin the compute back up, which costs latency at a moment a user is waiting.

Self-hosted Postgres on a VPS is always on. There is no wake-up, no cold start, and the database sits a few milliseconds of network latency from your application if both run on the same box or the same datacenter. The community reports dramatic p95 gains after moving from serverless with pooling to a local instance behind a connection pooler: one write-up describes p95 latency dropping from the 900 ms range to single-digit milliseconds after switching to self-hosted Postgres with Docker PgBouncer.[8] We have not measured this on our own stack yet, so take that as a signal, not our benchmark, but the direction is consistent with how scale-to-zero works.

The flip side: if you genuinely need to scale to zero to avoid paying for an idle database, managed is designed exactly for that. And if your database and application are in different regions or you need multi-node availability, a single VPS is a poor fit and managed (or a properly replicated self-hosted setup) becomes the right call.

Why PgBouncer, and how transaction pooling works

Connecting an application directly to Postgres is fine until you have many clients. Each client connection consumes a backend process and memory on the server, and Postgres has a hard max_connections limit. PgBouncer sits in front of Postgres and lets thousands of clients share a small number of real server connections.

PgBouncer has three pooling modes:[7]

  • Session pooling (the default): a server connection is assigned to a client for the whole time it is connected, and returned to the pool when the client disconnects.[7]
  • Transaction pooling: a server connection is assigned to a client only for the duration of a single transaction, then returned to the pool.[7] This is what lets one server connection serve many concurrent clients, because most connections spend most of their time idle between transactions.
  • Statement pooling: the server connection is returned after each query; it breaks transaction semantics and is rarely appropriate.[7]

Transaction pooling is the mode that actually solves the connection problem. Because a connection is only held during a transaction, a pool of, say, 20 server connections can serve hundreds or thousands of idle clients. The documented default server pool size per user/database is 20, and the default maximum number of client connections is 100.[7]

There is a cost: in transaction pooling, session state does not persist across transactions. SET statements, session-level advisory locks, and prepared statements are not supported the way they are in session mode, because each transaction may run on a different server connection.[9] Most web applications (REST/GraphQL APIs, short request handlers) work fine with transaction pooling; long-lived interactive sessions that rely on session state need session pooling.

A minimal PgBouncer setup

The classic file is pgbouncer.ini. PgBouncer listens on port 6432 by default.[7] A minimal configuration to enable transaction pooling:

[databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
pool_mode = transaction
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
max_client_conn = 1000
default_pool_size = 20

Then point your application at 127.0.0.1:6432 instead of 5432, populate /etc/pgbouncer/userlist.txt with a SCRAM or md5 password hash, and start PgBouncer with pgbouncer -d /etc/pgbouncer/pgbouncer.ini.

Because transaction pooling requires server_reset_query to clean up session state between clients, keep an eye on PgBouncer’s configuration for server_reset_query and make sure your Postgres config allows the pooler to manage connections. The exact values depend on your workload, but the mode itself is the important architectural decision.

So what does it cost, in practice?

Let’s put numbers side by side for a small, always-on production Postgres that has, say, 2 GB of RAM working set and runs 24/7.

Setup Effective monthly cost Notes
Supabase Pro, database-only, Micro compute $25/mo[3] Includes $10 compute credit; micro compute covers the base project.[3]
Neon Launch, 2 CU always on ~$155/mo 2 CU × 24 h × 30 days × $0.106/CU-h ≈ $153 + storage.[2]
RDS Single-AZ, ~2 vCPU (e.g. db.t4g.small/medium class) per-hour instance + storage[4] Free tier only covers 750 h for a year.[4]
Hetzner Cloud VPS (self-hosted PG + PgBouncer) from €5.99/mo[5][7] Plus IPv4 address and your ops time.
DigitalOcean Droplet (self-hosted PG + PgBouncer) from $4/mo[6] 1 GB; pick 2 GB for a real DB.

The Neon number is illustrative, not a quote: it assumes a fixed 2-CU always-on compute, which is exactly the pattern you do not pay for if you let it scale to zero. If you keep the compute suspended between bursts, Neon’s bill collapses to storage. That is its whole design.

The point is that an always-on 2 GB database on a $5-6 VPS costs a fraction of what an equivalent always-on managed instance costs, and it is always warm. The managed providers win when your workload is bursty, unpredictable, or when you do not want to operate Postgres at all.

A decision table

Your situation Better choice
Steady, always-on production DB, happy to run a VPS VPS + PostgreSQL + PgBouncer
Workload is bursty and you can scale to zero Managed (Neon/Supabase)
You want zero database administration, backups, patching handled Managed
You need database and app co-located for lowest latency VPS (same box or same DC)
You need multi-region or managed replication/HA Managed, or replicated self-hosted clusters
Dev/staging with tiny, idle databases Managed free tiers (Neon Free, Supabase Free)

For a self-hosted production path, PostgreSQL 16 or 18 plus PgBouncer in transaction pooling mode on a $4-6 VPS is a legitimate, cheaper alternative to a managed database for steady workloads. It costs you operations time and you own the backups, monitoring, and upgrades. When those are things you would rather buy than run, managed Postgres is the right price.

Sources

  1. PostgreSQL - Versioning Policy (major version support, PG18 18.6 current, PG14 EOL 2026-11-12). https://www.postgresql.org/support/versioning/ - checked 2026-10-31
  2. Neon - Pricing (CU definition, CU-hour rates, scale-to-zero, storage, egress). https://neon.tech/pricing - checked 2026-10-31
  3. Supabase - Pricing (Pro plan, compute credits, free tier, pause policy). https://supabase.com/pricing - checked 2026-10-31
  4. Amazon RDS for PostgreSQL - Pricing (on-demand instances, hourly billing, 10-min minimum, Multi-AZ, free tier, storage). https://aws.amazon.com/rds/postgresql/pricing/ - checked 2026-10-31
  5. Hetzner Cloud (Cost-Optimized from €5.99/mo, Regular Performance from €6.49/mo). https://www.hetzner.com/cloud/ - checked 2026-10-31
  6. DigitalOcean - Pricing (Droplets from $4/mo). https://www.digitalocean.com/pricing - checked 2026-10-31
  7. PgBouncer - Config (listen_port 6432, pool_mode session/transaction/statement, default_pool_size 20, max_client_conn 100). https://www.pgbouncer.org/config.html - checked 2026-10-31
  8. PgBouncer - Features (pooling modes, transaction pooling assigns server connection only during a transaction). https://www.pgbouncer.org/features.html - checked 2026-10-31
  9. xsypher.com - “Why we moved from serverless postgres to self-hosted docker pgbouncer” (community-reported p95 latency signal; not our measurement). https://xsypher.com/article/why-we-moved-from-serverless-postgres-to-self-hosted-docker-pgbouncer-914ms-to-2ms-p95 - checked 2026-10-31