Every managed PostgreSQL comparison page looks the same because every provider clears the same checklist. Backups: yes. High availability: yes. pgvector: yes, everyone, as of about two years ago. The checklist tells you nothing. The differences that cost you, in p99 latency, in a bill that triples in month two, in a Saturday spent discovering your "backup" was a daily snapshot, live one layer down, in questions the pricing page is built to never answer directly.
I've spent the last year benchmarking Postgres on various infrastructure, and enough time on the buying side before that to know which questions separate providers. Here they are, roughly in the order they'll hurt you.
Is the compute actually yours?#
First question, asked verbatim to support: "If I run a CPU-bound query for an hour, do I get the cores on the pricing page, the whole time?"
Plenty of entry tiers run on shared or burstable compute. That's fine for a prototype (every free tier is shared, including ours) but it's a different product from dedicated cores, and pricing pages rarely make the boundary obvious. Burstable instances accumulate CPU credits when idle and spend them under load. Your day-one benchmark runs on a full credit balance and looks great. Your month-three index rebuild runs on an empty one and crawls.
This matters more than it used to because modern Postgres workloads have long CPU-bound phases. An HNSW index build pegs cores for somewhere between minutes and hours. I've watched a 500k-vector build sit at 100% CPU for over four hours on a 4 GB machine, and that was with dedicated cores and the build being memory-bound. On throttled shared compute you can't even predict the duration, which means you can't schedule maintenance windows.
If the answer to the verbatim question involves the words "burst," "baseline," or "fair use," price the next tier up. That's the real price.
What's physically under the data directory?#
Nobody volunteers this, and it's the single biggest performance variable between providers: local NVMe attached to the machine, or network block storage (EBS and its cousins)?
The honest version of the tradeoff: when your working set fits in RAM, storage barely matters, because Postgres serves from cache either way. The difference shows up exactly when things go wrong. A cold cache after a failover. A working set that grew past memory. An index scan that touches pages nobody has read in a week. Network block storage answers each random read with a network round trip; local NVMe answers in microseconds. That gap is your p99 during the incident, which is the only time anyone looks at p99.
Network storage has real advantages (instant snapshots, detach-and-reattach, easy resizing) and providers built on it lean on those. The question is whether you're paying for them with tail latency. We measured this gap directly and published the full methodology in our NVMe vs cloud-SSD benchmark post; the short version is that index-heavy and vector workloads are exactly the shape that exposes it.
One follow-up if the answer is local NVMe: ask how durability works, since you can't snapshot a local disk from the outside. The good answer involves streaming replication plus a real backup tool (pgBackRest or similar) shipping to separate storage. "We rsync nightly" is not a good answer.
PITR: included, or the second bill?#
There's a pattern in managed database pricing: backups are included, but point-in-time recovery is an add-on, and retention beyond a few days is another add-on. Read the fine print, because the difference between "daily backup" and "PITR" is the difference between losing up to 24 hours of writes and losing none.
Most real data loss isn't a dead disk. It's a migration that ran against prod, a DELETE missing its WHERE, a deploy that wrote garbage for forty minutes before anyone noticed. Daily snapshots are useless against all of these. You need to restore to 14:32, not to midnight. PITR (continuous WAL archiving plus base backups) gives you that, and the tooling underneath is mature open source. When a provider charges meaningfully extra for it, that's a margin decision, not a cost one. Treat it as a signal; PITR in the base price at $29/month exists, so hold everyone to that.
And then, this is the part everyone skips, restore one during the trial. A backup you've never restored is a hypothesis.
Can you see real metrics?#
Some provider dashboards show a CPU graph and a disk graph and call it monitoring. That's enough to confirm the server exists and nothing else. During an incident you need queries per second, latency percentiles, cache hit ratio, connection counts, and replication lag, and you need history, because the question is always "when did this start?"
The minimum bar: QPS and p50 visible without leaving the dashboard. The better bar: raw Prometheus metrics you can pull into your own Grafana, so the database stops being a black box in an otherwise observable system. If a provider can't show you query latency percentiles, you will be debugging your worst outage with EXPLAIN ANALYZE and vibes.
Pricing models: flat boredom beats metered excitement#
Two models dominate. Flat per-node: you pay for a machine, the day-30 bill matches the day-1 quote. Usage-metered: you pay per compute-second, per IOP, per GB stored and per GB egressed, and the entry price is delightfully low.
Metered pricing isn't a scam. It's genuinely cheaper for a side project that gets nine requests a day. But it transfers all forecasting risk to you, and database workloads are terrible at being forecast. The failure mode is always the same: some line item you didn't model, whether IOPS during a bulk load, egress during a migration, or autoscaled compute during a spike, dominates the bill. Vector search is especially exposed: ANN queries are pure read QPS, so a metered provider bills you for exactly the thing you're trying to scale.
Do this exercise before signing anything: take your peak day, multiply by 31, and price it on the metered plan. If the number makes you wince, you've found your answer. If you can't compute the number because the pricing page has too many variables, that's also your answer.
We went flat at Rivestack ($29 to $159 per node, nothing metered) precisely because we got tired of doing that exercise as customers. But the model matters more than the vendor: steady or growing traffic wants flat; only genuinely spiky, near-zero workloads come out ahead on metering.
The benchmark-marketing trap#
This one deserves its own section because it's how vector database marketing in particular gets away with murder. A QPS number without two disclosures attached is not a measurement, it's an ad.
Disclosure one: recall. Approximate nearest neighbor search trades accuracy for speed on a dial (ef_search in HNSW terms). On our entry Solo node, the same 250k×1536 dataset gives us ~980 QPS at recall@10 of 0.90 (4 clients); turn the dial the other way and I can push the headline QPS well past that by silently making the answers worse, and you'd never know unless recall was reported. Any QPS figure without recall@k next to it should be read as "best case at unknown quality."
Disclosure two: where the client ran. A benchmark client on the same host hides the network entirely. Ours run same-region, client to server over TLS through the pooler, and that adds about 0.4 ms, which is honest, because that's what your app will see. Cross-region would add tens of milliseconds. If the methodology doesn't say where the load generator was, the latency numbers are decorative.
Two more tells. First, peak QPS paired with minimum p50 in the same sentence: those almost always come from different runs (our Solo ~980 QPS run had a 3.8 ms p50 at 4 clients; the runs that post higher throughput do it by piling on clients, which pushes latency up, so pairing the peak QPS with the minimum p50 would be fiction). Second, capacity claims that skip the index build: a million 1536-dim vectors won't even build an HNSW index on a 4 GB node (it OOMs), so any "1M vectors on our smallest plan" claim means a plan with real memory or a benchmark that never ran. And quality has a cliff, too: push a 4 GB node toward recall ~0.99 with a high ef_search and the working set spills cache, so throughput collapses by an order of magnitude and tail latency balloons. Same box that served ~1,600 QPS at recall 0.93 (16 clients).
The defense is to run your own numbers. We open-sourced pgvector-bench for exactly this: point it at any Postgres with pgvector, yours or a competitor's. And if you do test through a connection pooler in transaction mode, know the gotcha that invalidates half of all DIY benchmarks: a session-level SET hnsw.ef_search is silently dropped between transactions. Your "tuned" run is running at defaults. Use SET LOCAL inside the query's transaction instead:
BEGIN;
SET LOCAL hnsw.ef_search = 80;
SELECT id, title
FROM documents
ORDER BY embedding <=> $1
LIMIT 10;
COMMIT;(More on this and the rest of the tuning surface in our HNSW tuning guide.) If you'd rather feel the latency than read about it, ask.rivestack.io is a live semantic search over Hacker News running on a real Rivestack database.
What "high availability" actually buys#
When you pay for a second node, ask exactly what it does. Two very different answers hide behind the same checkbox: standbys that exist purely for automatic failover, and replicas that also serve read traffic.
I'll hold us to our own standard here: on Rivestack today, added nodes are streaming-replication standbys for automatic failover only. The load balancer routes every query, reads included, to the primary. Read-serving replicas are roadmap, not product. So when a salesperson anywhere implies "add nodes to scale reads," pin them down, because a lot of managed Postgres HA works the way ours does and the marketing rarely admits it. HA buys you uptime. It does not buy you throughput.
EU residency is jurisdiction, not a region picker#
If you have European users or a DPA to satisfy, "we have an EU region" is the beginning of the conversation, not the end. Three questions:
- Do backups and logs stay in-region too? Plenty of setups keep the primary in Frankfurt and ship backups somewhere cheaper. Your data residency is wherever the copies are.
- Who are the sub-processors? Monitoring, support tooling, and billing systems all touch metadata. The DPA appendix lists them; read it.
- Who owns the operator? This is the one teams miss. A US-owned company running an EU region remains subject to US jurisdiction, CLOUD Act included: server location doesn't change who can be compelled to produce data. If your compliance posture requires data outside US legal reach, you need an EU operator on EU soil. (Ours runs in Falkenstein, Germany, for exactly this reason.)
There's also the unglamorous version of residency: latency. Put the database in the same region as your app servers. Every query pays the round trip; 0.4 ms same-region is noise, a transatlantic hop is not.
The exit door, inspected before you enter#
Migration in is easy everywhere: pg_dump/pg_restore for small databases, logical replication for low-downtime cutovers. The real question is the other direction: when you leave in two years, what does that look like?
The test is simple and takes twenty minutes during your trial:
pg_dump "postgres://user:pass@trial-host:5432/app" -Fc -f exit-test.dump
pg_restore -d "postgres://localhost:5432/app_check" exit-test.dumpIf that round-trips cleanly, you're dealing with real Postgres. If the provider steers you toward a proprietary export, custom extensions you can't install elsewhere, or an API-only data path, you're not buying a database, you're buying a dependency.
One caveat for vector workloads: dump/restore moves your data fine, but indexes rebuild on the target, and HNSW rebuilds are memory-bound and slow on small nodes (around 5 minutes for 250k vectors on a 4 GB machine; hours for 500k). Budget the window, or use logical replication so the index builds while the old system still serves. Some providers, us included, will run the whole migration for you free. Take them up on it. The same logic applies when leaving a platform bundle: the Supabase exit path is mostly the same pg_dump, with extensions as the wrinkle.
The trial week I'd actually run#
Stop reading comparison pages. Spend one week and the cost of the smallest paid tier:
- Day 1, ask the two questions. Dedicated or shared compute, and local NVMe or network block storage, verbatim, to a human. Evasive answers are answers.
- Day 2, load real data at full scale. Not a 10% sample. Build your largest index and time it. This is where "1M vectors, no problem" claims go to die.
- Day 3, benchmark from where your app lives. Same region, realistic concurrency, and if vectors are involved, measure recall@10 against exact KNN, not just QPS. pgvector-bench does this out of the box.
- Day 4, break something. Restore to ten minutes ago via PITR. Time it. Note whether it was self-serve or a support ticket.
- Day 5, kill the primary (if you're paying for HA) and watch what your connection string experiences. Then check whether reads ever touched the standby. They probably didn't.
- Day 6, compute the worst-case bill. Peak day × 31 on their pricing model. Every metered line item.
- Day 7, run the exit test. Dump, restore elsewhere, diff row counts.
A provider that survives all seven is a keeper regardless of whose logo is on it. Most won't survive day 4 and day 6 both, and that's the whole game: the failure modes cluster, because the same incentives that meter your IOPS also upsell your PITR.
Frequently asked questions#
Is local NVMe always faster than network block storage?
For a working set that fits entirely in RAM, no: Postgres serves from shared buffers and the page cache, and the disk barely participates. The gap opens on cold caches (post-failover, post-restart), working sets larger than memory, and index-heavy access patterns like vector search, where network storage pays a round trip per random read. Network block storage does buy you operational conveniences (snapshots, resizing, detach/reattach), so the honest framing is: you're choosing between tail latency and storage flexibility. For latency-sensitive production databases, I take the NVMe.
How can I tell whether a database benchmark is honest?
Check for five disclosures: recall@k alongside any ANN throughput number, client count, where the benchmark client ran relative to the server, whether the quoted latency comes from the same run as the quoted QPS, and whether the index actually built on the advertised hardware tier. Missing any one of these lets the vendor inflate results without technically lying. Bonus points if the tooling is open source so you can reproduce the run yourself.
Is point-in-time recovery worth paying extra for?
It's worth having unconditionally: most data loss is a bad deploy or a fat-fingered query, and daily snapshots can lose up to 24 hours of writes against those. Whether it's worth paying extra for is the wrong question: the mechanism is mature open-source tooling, so a premium charge for it tells you about the provider's pricing philosophy. Flat plans with 7 to 14 days of PITR exist at $29 to $49/month; that's a fair bar to hold everyone to.
Do more nodes mean my reads scale?
Usually not, and this is the most common HA misunderstanding. In many managed Postgres setups, including Rivestack's today, additional nodes are streaming-replication standbys that exist solely for automatic failover; the load balancer sends all traffic, reads included, to the primary. Read-serving replicas are a separate feature with separate consistency tradeoffs (replication lag means a replica can serve stale rows). Ask the provider directly: "does any read traffic reach the standby?" If the answer is no, added nodes buy you uptime, not throughput, so budget capacity accordingly.
# keep reading