postgres / hosting

I run my work app on Render with a managed Postgres database. This article records how I compare managed Postgres providers for that database. I have not chosen one yet.

The workload

A fresh restore of the production database is about 5 GB. Indexes are about a third of it. The largest tables are derived caches, which a job rebuilds every 15 minutes. The job queue table is almost as large, and workers insert and delete its rows all day.

The primary has 4 vCPUs and 16 GB of RAM. One standby gives high availability (HA). These queries show the size and the share of reads that shared_buffers serves:

SELECT pg_size_pretty(pg_database_size(current_database()));

SELECT round(100.0 * blks_hit / nullif(blks_hit + blks_read, 0), 2) AS cache_hit_pct
FROM pg_stat_database
WHERE datname = current_database();

A read that misses shared_buffers can still come from the operating system's page cache, so this ratio counts more disk reads than the disk sees.

On production, the databases take about 12 GB and the WAL about 2 GB of a 75 GB disk. The restore packs each table tight, so it is smaller. On production, autovacuum keeps up, and the job queue table holds few dead rows. Its file still holds about 3 GB of free space from an earlier peak. Vacuum returns empty pages at the end of a file, and it keeps the free space inside the file for reuse.

Over 30 days, the provider's metrics show:

The disk has a lot of headroom today. Each commit still waits for a WAL flush, and checkpoints, vacuum, and the cache rebuilds write in the background.

A staging database runs next to production, with 1 vCPU, 4 GB of RAM, and no standby. I refresh it from a fork of production.

What I need

A provider must meet each of these. I compare price and operations only among the providers that do.

My app connects as a role that owns its tables. A separate admin role runs the operations above.

Admin operations

My schema and these operations need no superuser, so I count superuser access as a preference. A provider fails this need only when it refuses an operation I use, and none of the four does.

Crunchy Bridge also creates an application role for the app, and one role for each team member, read-only by default. pgaudit logs each statement that the superuser and the member roles run. On Ubicloud, I create the app role and the member roles myself, and I turn on pgaudit for them.

Extensions

All four providers offer the four extensions my schema uses. PlanetScale enables pg_stat_statements from its dashboard, with a restart. Ubicloud and Render also offer pgaudit, pg_repack, and pgvector.

A dump and restore rebuilds each index under the new server's collation and unaccent rules. So I move between providers with a dump and restore, and I check the list on the new server first:

SELECT name, default_version
FROM pg_available_extensions
WHERE name IN ('btree_gist', 'pg_stat_statements', 'pg_trgm', 'unaccent');

Storage

A network disk such as AWS EBS survives the loss of its server. Each read that misses memory and each WAL flush crosses the network.

Local NVMe sits in the server, so a read takes tens of microseconds. The disk dies with the server. So the provider keeps the data with standbys and a WAL archive in object storage, not with the disk.

My database uses a small part of its IOPS, so NVMe gives it little today. A published benchmark measured about 9 times more transactions per second on NVMe than on 3,000-IOPS EBS. It ran random updates on a 482 GB table with 64 GB of RAM. My workload is different, so I read it as a limit for a database much larger than its RAM.

Price

Prices that I read in November 2026, in Virginia, for 4 vCPUs and 16 GB with one standby and about 64 GB of storage. The Ubicloud prices are quotes from its console for the Virginia regions. Its public pricing page lists Germany prices: standard-4 with 128 GB of storage is $156 for each node. The others come from each console or pricing page.

Provider Size Nodes Disk Monthly
Ubicloud Leaseweb standard-4 2 NVMe $287
Render 4c-16g 2 EBS $438
Ubicloud AWS m8gd.xlarge 2 NVMe $539
PlanetScale Metal M-160 ARM 3 NVMe $570
Crunchy Bridge Standard-16 2 EBS $573

My October bill for production was $571: $560 for the compute of both nodes and about $0.20 for each GB of storage, which covers both nodes. At 75 GB, production costs $575 a month.

Ubicloud gives about twice the compute for each dollar. For what I pay now, Ubicloud Leaseweb sells standard-8: 8 vCPUs, 32 GB, 256 GB of storage, and a standby, for about $602.

For staging, with 4 GB of RAM and no standby:

Provider Size vCPUs Storage Monthly
PlanetScale PS-40 ARM 1/2 10 GB $28
Ubicloud Leaseweb hobby-2 2 shared 32 GB $38
Render 1c-4g 1 50 GB $70
Crunchy Bridge Standard-4 1 50 GB $75

PlanetScale bills storage over 10 GB on top of that price. A single node on PlanetScale runs on EBS.

Replication and writes

I set two limits apart: the writes I can lose in a failover, and the time the app can be down. Synchronous replication bounds the first. A second standby helps the second.

A faster local flush can offset the round trip. I have not measured how the two compare for my write load.

Forks and upgrades

A fork is a new database that the provider restores from the backups and WAL archive of another database, to a minute that I choose.

Crunchy Bridge and Ubicloud upgrade a major version the same way. The provider builds a new server while the old one serves traffic. In the next maintenance window, it stops writes, runs pg_upgrade on the new server, and moves traffic to it. Each console offers a fork for a test upgrade first.

Render upgrades in place, and the database is down for the upgrade, up to an hour. It suggests a test upgrade on a clone first. PlanetScale has no in-place major upgrade. I would create a Postgres 18 database and migrate to it online, with logical replication.

Crunchy Bridge keeps the old server if pg_upgrade fails. Its CLI schedules the upgrade:

cb upgrade start --cluster <cluster-id> --version 18 --starting-from '<utc-time>'

Observability

Crunchy Bridge shows query insights in its console: outlier queries, cache and index hit rates, bloat, vacuum statistics, and unused indexes. Ubicloud shows host and database metrics and searchable logs, and it exports both to Prometheus and OpenTelemetry. It has no pages for query insights. With a superuser, I can run the same queries on pg_stat_statements and the pg_stat_* views myself.

PlanetScale has the richest tools: Query Insights in its console, and pscale inspect for outliers, bloat, vacuum statistics, and unused indexes. Render shows metrics and logs each query that runs longer than 2 seconds.

Access and audit

My company's IT team requires SSO for each admin console.

Each of the four has a SOC 2 Type 2 report and keeps an audit log of console actions:

Support

I judge support by how a provider acts in an incident: a failover that does not complete, a failed upgrade, or a slow restore. A how-to answer does not test that, and an AI model now answers most how-to questions. Before a move, I ask the provider for its response time on a production-down ticket, who acts when an automatic failover stalls, and whether an engineer can watch the cutover.

Network path

Render Postgres connects over Render's private network. That internal route serves a self-signed certificate and does not support sslmode=verify-full. My apps refuse to start without it (see postgres / connection).

Render's external route crosses the public internet. In November 2026, it presented a Let's Encrypt certificate for *.virginia-postgres.render.com, and OpenSSL verified the chain and the hostname against the system trust store:

openssl s_client -starttls postgres -connect "$HOST:5432" \
  -servername "$HOST" -verify_hostname "$HOST" -verify_return_error

So sslmode=verify-full with sslrootcert=system passes on that route. I have not checked channel binding and SCRAM on it.

Render Private Link connects my Render private network to an AWS endpoint service in the same region. It needs a Pro workspace and costs $30 a month for up to three links, plus $0.03 for each GB out.

Ubicloud Leaseweb is outside AWS, so the path crosses the public internet. Verified TLS and SCRAM protect the traffic, and the database firewall limits the source addresses.

Other Render customers in a region share Render's default outbound IP ranges. A database firewall that admits those ranges lets those customers attempt to log in. A dedicated IP set gives my workspace three outbound IPv4 addresses, and the firewall can admit only those. It needs a Pro workspace and costs $100 a month for each set. It changes outbound traffic only.

I weigh verified TLS, least privilege, and tested restores above a private network path. Private Link adds a boundary. I do not need it to meet my minimums.

Typical round-trip times from Render Virginia, which runs in AWS us-east-1, are estimates until I measure them:

Database Round trip
Same AWS region, same zone under 1 ms
Same AWS region, another zone 1 to 2 ms
Ubicloud Leaseweb, Manassas 1 to 4 ms
Another region, Ohio to Virginia 11 to 15 ms

A page that runs its queries one after another pays the round trip for each one. I measure from a Render shell with pgbench. The first run keeps one connection open. The second run opens a new connection for each query, so the difference is the cost of the TLS handshake and authentication:

echo 'SELECT 1;' > select1.sql
pgbench -n -f select1.sql -c 1 -T 30 "$DATABASE_URL"
pgbench -n -f select1.sql -c 1 -T 30 -C "$DATABASE_URL"

Where I am

My database uses a small part of its IOPS, so I would not pay for a faster disk. Crunchy Bridge, Ubicloud, and PlanetScale meet each of my minimums. Render's internal route fails verified TLS. Its external route passes verify-full but crosses the public internet, and its SSO needs the Scale plan.

I put operations ahead of headroom. A cheaper database is a bad trade if I become the operator for each failed upgrade, restore, or stalled query. I already run a smaller database for another app on Ubicloud Leaseweb, with one standby.

So I keep production where it is while I test Ubicloud:

  1. Restore a recent dump into a Ubicloud Leaseweb standard-4 with one standby, and point the staging app at it. First, check the extensions, create the app and member roles, and set up SSO.
  2. Measure p95 and p99 of real app requests and of commits, against staging on Crunchy Bridge.
  3. Rehearse a fork, a restore, a test upgrade, and a failover while the app reconnects.

I move production only for a benefit those numbers show.

My Render services run in Ohio. Both Ubicloud options are in Virginia, so a move would take the services to Render's Virginia region with the database.

← All articles