Managed database pricing is advertised as memory, vCPU, and storage, and those three rarely account for the whole bill. The lines that surprise people are egress, backup retention, the standby replica that high availability quietly doubles, and provisioned IOPS on providers that meter disk performance separately.
Comparing database plans by reading the resource table gets you a number that is right for the compute and wrong for the total. That is not because anyone is hiding anything — the extras are documented — but because they appear on separate pages and are easy to omit when the plans in front of you all look like the same three columns.
Table of contents
- The line items
- Egress, and why it catches people
- Sizing without over-buying
- Connections, and the cost hidden behind them
- Managed versus running it yourself
- Reducing the bill without reducing the service
- How this fits the rest of the stack
- FAQ
The line items
In roughly the order they appear on a bill.
Compute — memory and vCPU. The headline figure. Memory usually dominates, because a database performs well when the working set fits in RAM and badly when it does not.
Storage. Charged per gigabyte per month, sometimes with a minimum tied to the plan. Two things to check: whether it grows automatically or requires a manual resize, and whether it can shrink. Frequently it cannot, so a one-off import that doubles your storage is a permanent cost.
IOPS. Some providers include disk performance in the plan and some meter it separately or sell provisioned IOPS as an add-on. This is easy to miss and can be a large share of the total for a write-heavy workload.
Backups. Sometimes a fixed retention is included with charges beyond it, sometimes billed per gigabyte from the first byte. Point-in-time recovery, which stores transaction logs continuously, generally costs more than daily snapshots.
High availability. A standby replica means a second instance, so the compute cost roughly doubles. This is the largest single jump on most bills and it is usually presented as a checkbox.
Read replicas. Each one is another full instance at close to the price of the primary.
Egress. Data leaving the provider’s network. Within the same private network it is usually free; across regions or out to the internet it is metered, and it is the most common source of a bill nobody predicted.
Support tiers, which on some providers are a percentage of spend and become significant at scale.
Egress, and why it catches people
Worth its own section because it is the line that produces genuine shock.
The rule is simple where you can apply it: traffic between your application and your database over a private network is free. Traffic leaving the provider is not.
The setups that generate unexpected egress:
- The application hosted with one provider and the database with another. Every single query crosses the internet, is metered, and adds latency on top.
- A database in one region and the application in another.
- Analytics tooling or a BI dashboard pulling large result sets out continuously.
- A nightly backup or export written to storage outside the provider.
- A development environment on someone’s laptop connected to the production database, pulling a table at a time.
The first one is the expensive mistake and it is made for sensible-sounding reasons — a better price on the database over here, a preferred platform for the app over there. The combined cost, including per-query latency, frequently exceeds what keeping them together would have cost, and the latency penalty is paid by every user on every page.
The rule worth following: put the database on the same private network as the application that queries it. Managed MySQL and Postgres on RunxBuild sit on the private network with the service by default, which is the arrangement you want rather than an optimisation to remember.
Sizing without over-buying
Most small applications are over-provisioned on CPU and under-provisioned on memory, because CPU is the number people compare and memory is the one that determines behaviour.
The heuristic that works: size memory so the active portion of your data — the tables and indexes actually queried — fits comfortably in the buffer cache. When it does, most reads are served from RAM and the database is fast. When it does not, reads hit disk and the difference is not subtle.
Measure rather than guess. On Postgres, the cache hit ratio tells you directly:
SELECT sum(heap_blks_hit) AS hit,
sum(heap_blks_read) AS read,
ROUND(100.0 * sum(heap_blks_hit) /
NULLIF(sum(heap_blks_hit) + sum(heap_blks_read), 0), 2) AS hit_pct
FROM pg_statio_user_tables;
Above 95 percent means memory is adequate. Persistently below suggests the working set has outgrown the instance, and more memory will do more than more cores.
On CPU, watch active connections and query time rather than raw utilisation. A database at 40 percent CPU with slow queries has an indexing problem, not a sizing problem, and buying a larger instance to fix a missing index is an expensive way to postpone the work.
Which is the general point: optimisation is frequently cheaper than a bigger plan. One index on a table being scanned repeatedly can reduce load by more than doubling the instance would, and it costs an afternoon rather than a monthly increase forever.
Connections, and the cost hidden behind them
Connection limits scale with plan size, and hitting one is a hard failure — new connections are refused rather than queued.
The trap is that the number of connections your application opens has little to do with how much traffic it serves. Each application instance keeps its own pool, so five instances with a pool of twenty each is a hundred connections whether they are busy or idle. Serverless deployments are worse, because each cold-started function may open its own.
Teams then upgrade to a larger database plan solely to raise the connection limit, paying for memory and cores they do not need to obtain a number.
A connection pooler in front of the database is the cheaper answer. It multiplexes many application connections onto a small number of real ones, and it frequently means a smaller instance is sufficient for the same workload.
Before upgrading a plan for connection headroom, check what is actually connected:
SELECT count(*) FILTER (WHERE state = 'active') AS active,
count(*) FILTER (WHERE state = 'idle') AS idle,
count(*) FILTER (WHERE state = 'idle in transaction') AS idle_in_txn,
count(*) AS total
FROM pg_stat_activity;
A large idle count means pool sizing, not database sizing. A non-zero idle in transaction count means application code holding transactions open across slow operations, which is a bug worth fixing regardless of cost.
Managed versus running it yourself
The comparison usually offered is a managed database’s monthly price against the cost of a VPS running Postgres, and on that basis self-hosting always wins. It is not the right comparison.
What a managed instance includes beyond the machine: automated backups with tested restore paths, point-in-time recovery, minor version patching, failover if you enable high availability, connection limits and user management as a control-plane feature, monitoring, and private networking configured for you.
Reproducing that on a VPS is not exotic — it is documented and many people do it — and it is a genuine ongoing commitment. Backups need configuring and testing. Patches need applying. Failover needs building. Somebody has to notice when the disk fills.
The honest framing: self-host if you have the operational capability and the time is either free or enjoyable. Use managed if the database holds something you cannot lose and nobody on the team wants to be the person who tests restores quarterly.
For reference on one side of it, managed MySQL and Postgres on RunxBuild are on the same plan ladder as services, from $4 on Dev and $6 on Basic upward, with backups, user management, connection limits, and private networking included rather than assembled.
Reducing the bill without reducing the service
- Add the missing index. The single highest-return action available, and it reduces load rather than capping it.
- Pool connections so the plan is sized for work rather than for a connection count.
- Keep the database on the same private network as the application, eliminating query egress and its latency.
- Right-size backup retention. Thirty days of point-in-time recovery is expensive and frequently more than the actual requirement.
- Question high availability on non-critical environments. A standby for staging doubles its cost to protect something nobody depends on.
- Archive cold data. Rows nobody queries still occupy storage and slow down maintenance operations.
- Check for read replicas nobody uses. They are easy to add during an incident and easy to forget afterwards.
- Set alerts on spend, not just on resources. A cost alert is the only thing that catches a runaway query loop before the invoice does.
The first two are worth doing before considering any plan change. A great deal of database spend is a missing index or a badly sized pool being paid for monthly rather than fixed once.
And measure before and after. It is easy to make a change, see the bill fall, and attribute it to the wrong thing — particularly when several changes ship together.
How this fits the rest of the stack
Database pricing is compute plus storage plus a handful of lines that are easy to omit when comparing plans — egress, backups, IOPS, and the standby that high availability adds. Size on memory rather than cores, pool your connections before buying headroom, and keep the database on the same private network as the application that queries it. The RunxBuild hosting calculator puts managed MySQL and Postgres next to the service and the storage, so the total is one view rather than three pricing pages.
Useful related references:
- GCP vs AWS: Pricing, Network, and When to Pick Each
- AWS vs GCP: Pricing, Network, and When to Pick Each
- AWS Config Pricing: The Bill That Surprises People, and Why
- Databases on RunxBuild
FAQ
What drives managed database pricing?
Memory and vCPU are the headline, with storage per gigabyte alongside. The lines that are easy to miss are egress when data leaves the provider network, backup retention, provisioned IOPS on providers that meter disk performance, and high availability, which adds a standby instance and roughly doubles the compute cost.
Why is my database egress bill so high?
Usually because the application and the database are with different providers or in different regions, so every query crosses a metered network. Analytics tools pulling large result sets and backups written outside the provider are the other common causes. Keeping the database on the same private network as the application makes query traffic free.
Should I pay for more memory or more CPU?
Memory, in most cases. A database is fast when the active data fits in the buffer cache and slow when it does not, so memory determines behaviour more directly than cores. Check the cache hit ratio — above 95 percent means memory is adequate and a CPU problem is more likely a missing index.
Is a managed database worth it over running Postgres on a VPS?
It depends on whether you want to own backups, restore testing, patching, failover, and monitoring. Managed includes all of that; a VPS includes the machine. Self-host if you have the operational capability and the time is free or enjoyable. Use managed if the data matters and nobody wants to be the person testing restores quarterly.
How do I reduce my database bill?
Add missing indexes first — that reduces load rather than capping it, and one index often does more than doubling the instance. Then pool connections so you are not buying a larger plan for a connection limit, keep the database on the private network with the application, and review backup retention and any read replicas nobody uses.