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:
- CPU at 20% to 40%, with a load average near 1.
- I/O wait under 1% of CPU time.
- A few hundred IOPS, against a baseline of 6,000.
- Memory at 45% to 70% used.
- About 70 connections, with no pooler.
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.
- Postgres 18.
- The extensions my schema uses:
btree_gist,pg_stat_statements,pg_trgm, andunaccent. - The admin operations I run: install those extensions, grant monitoring roles to a separate login, and set up logical replication for a cutover.
- A standby, and a stated limit on lost writes after a failover.
- Point-in-time restore, and a fork of production as a new database.
- A major version upgrade that I can test on a fork first.
- Verified TLS with channel binding and SCRAM (see postgres / connection).
- A short network path from my Render services.
- Console login through my company's identity provider, with SSO enforced.
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 gives
a
postgressuperuser. - Ubicloud gives a superuser connection string.
- PlanetScale
gives no
SUPERUSER. Its default role hasCREATEROLE,REPLICATION, andBYPASSRLS, and it holdspg_monitorandpg_maintain. It installs extensions from a list that it curates. - Render gives a database owner with
CREATEROLE. A feature request reports that this role cannot grantpg_monitoron Postgres 16 and later.
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.
- Crunchy Bridge and Render Postgres use EBS. Crunchy Bridge shows 6,000 baseline and 40,000 maximum IOPS at 4 vCPUs.
- Ubicloud uses local NVMe on its own servers at Leaseweb in Manassas,
Virginia, and on AWS
m8gdinstances inus-east-1. - PlanetScale Metal uses local NVMe on AWS.
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 |
- Ubicloud, Render, and Crunchy Bridge bill a standby at the price of the primary, storage included.
- PlanetScale Metal sells no node with 4 vCPUs and 16 GB.
M-160has 2 ARM vCPUs, 16 GB, and 100 GB of NVMe.M-320has 4 ARM vCPUs and 32 GB for $1,149.
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.
- Crunchy Bridge, Render, and Ubicloud with one standby replicate asynchronously by default. A commit waits only for the WAL flush on the primary. A failover can lose the last commits.
- Ubicloud can run one standby synchronously on request. With two standbys, it replicates synchronously by default, and a commit waits for one of the two.
- PlanetScale always runs two replicas with semi-synchronous replication. A commit waits until a replica in a second availability zone has the WAL. Each commit then pays a round trip between zones. Metal has no single-node option. A single node runs on EBS only.
- Render fails over when the primary is down for 30 seconds. Its HA proxy adds about 1 ms to each query. PlanetScale reports failovers in seconds.
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 forks to any minute in the last 10 days.
- Ubicloud forks to any minute in the last 7 days.
- Render restores into a new database, to any minute in the last 3 or 7 days by workspace plan, and up to 10 minutes before now.
- PlanetScale restores to any minute into a new branch, by default in the last 2 days and up to 5 minutes before now.
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.
- Crunchy Bridge enforces SSO through Microsoft Entra ID.
- Ubicloud signs in through OpenID Connect and can lock an email domain to that provider. Its open-source control plane has no SCIM, so I remove a person from the project by hand. Ubicloud staff can set a project flag that requires MFA or an OpenID Connect login from each member.
- Render offers SAML SSO and SCIM on its Scale workspace plan and higher.
- PlanetScale offers SAML SSO and directory sync as a $199 a month add-on.
Each of the four has a SOC 2 Type 2 report and keeps an audit log of console actions:
- Crunchy Bridge keeps team and cluster events for 90 days.
- Ubicloud shows its audit log in the console, the API, and the CLI.
- PlanetScale keeps its audit log for 15 days.
- Render keeps at least 90 days. Its audit log and its SOC 2 report need a Pro workspace.
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.
- Render documents the Crunchy Bridge setup.
- Ubicloud's API
creates a PrivateLink service
for a database in an AWS location. Ubicloud requires acceptance of
each endpoint. The setup allows Render's ARN principal and approves
the endpoint ID of the Render link, the steps that Render documents
for an endpoint service. The server certificate carries the private
hostname. A client reaches that name through the endpoint only when
the endpoint resolves the
private DNS name.
Render's docs connect through the DNS name of the link. So I have not
confirmed that
verify-fullworks on this path. - PlanetScale offers private connections through AWS PrivateLink.
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.
- PlanetScale always runs three nodes, so the same RAM costs about twice the Ubicloud Leaseweb price. It has no in-place major upgrade.
- Crunchy Bridge and Ubicloud fork and upgrade the same way, so a move between them keeps how I refresh staging and rehearse an upgrade.
- Ubicloud Leaseweb costs about half as much as Crunchy Bridge at the same size. By my estimate, until I measure it, each query round trip costs 1 to 3 ms more over the public internet.
- Ubicloud AWS with Render Private Link keeps the traffic in AWS for about the Crunchy Bridge price, with NVMe.
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:
- Restore a recent dump into a Ubicloud Leaseweb
standard-4with one standby, and point the staging app at it. First, check the extensions, create the app and member roles, and set up SSO. - Measure p95 and p99 of real app requests and of commits, against staging on Crunchy Bridge.
- 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.