What a Postgres, Redis, broker and search stack costs at rest
Passion, 26 May 2026
The interesting number for a conventional backend is not what it costs at scale. Scale is a nice problem and the finance conversation looks after itself. The interesting number is what it costs at zero: no users, no traffic, nothing happening, four stateful services sitting there billing by the hour because none of them can be turned off without losing your data or your index.
We keep a platform codebase built the conventional way: NestJS on PostgreSQL, with Redis, a message broker and a search cluster behind it. Most of our own products run on serverless infrastructure instead. Having both is useful mainly because it makes the trade concrete rather than ideological, and because we can be honest about what the conventional one would cost us if we ran it, which we do not.
The floor
| Piece | Why it is there | Can it idle at zero? |
|---|---|---|
| PostgreSQL | joins, transactions, constraints, the actual data | no, and you want a replica |
| Redis | cache, rate limits, session and lock state | no |
| Message broker | durable events, consumer groups, replay | no |
| Search cluster | ranked full text, analysers, facets | no, and it wants an odd number of nodes |
| Orchestrator and ingress | running all of the above | no |
| Log and metric storage | knowing what happened | grows whether or not anyone visits |
Six line items before a single person has signed up. The search cluster is normally the largest of them, because search is memory-hungry and a single node cluster is a cluster you will regret during your first failover. That is the part teams underestimate: they price the database, remember Redis, forget that the search tier costs more than both.
Then there is a seventh line item that is not on the invoice and is usually bigger than the rest combined. Somebody has to upgrade Postgres major versions, rotate credentials, watch disk fill, apply the orchestrator patch that cannot be skipped, and answer the phone when the broker wedges. For a team of three that is not a line of spend, it is a fraction of a person, and it is the fraction that gets taken from the work customers pay for.
What the floor buys you
That list is not extravagance. Each of those services is there because something in the design genuinely needs it.
A relational database with real joins is worth a great deal when the model is genuinely relational. Ours runs to roughly 45 models across a 916 line schema: people, tenants, memberships, connections, posts, comments, jobs, applications. Flattening that into something document-shaped costs more in application code than it saves in infrastructure.
Full text search with proper ranking, stemming and facets is not something you approximate. Either you need a search tier or you do not, and the honest test is whether "closest substring match" would embarrass you in front of a user.
A broker gives you durable side effects. When somebody accepts a connection request, the notification, the feed entry and the search index update are consequences of that action rather than parts of the transaction. Ours travel over the broker for exactly that reason, and the search index write goes through an outbox table so that indexing can fail and be retried without ever putting the original operation at risk. That pattern costs a table and a worker and removes an entire class of the worst kind of bug, the half-succeeded user action.
The serverless column
| Conventional piece | Serverless substitute | What you give up |
|---|---|---|
| PostgreSQL | SQLite at the edge, or serverless Postgres | long transactions, connection-heavy patterns, some extensions |
| Redis | a key-value store with expiry | sorted sets, scripting, sub-millisecond adjacency |
| Broker | a managed queue | replay, consumer groups, ordering guarantees |
| Search cluster | a full text index in the database, or hosted search | ranking control, facets, language analysers |
| Orchestrator | nothing | the ability to run arbitrary processes |
The substitutions are real but they are not equivalences. Anyone who tells you a queue is a broker has not needed to replay three days of events. What you get in exchange is a bill that starts near zero, no patching, no capacity planning and no 3am page about a disk.
Serverless has its own hidden costs, and they are the mirror image. Per-request limits shape your code. Some query patterns are simply unavailable, and you find out which ones late. Local development is a different flavour of awkward rather than an easier one. The data layer is where the lock-in lives, and it is much stickier than the compute layer everybody worries about.
Choosing, in five questions
- Is the traffic flat or spiky? Flat and heavy favours servers, because you can buy capacity in advance at a discount. Spiky, low, or unknown favours per-request billing, because you are not paying for the peak all month.
- Does the query shape need joins and transactions across many tables? If yes, that is a database decision, and it constrains everything above it.
- Do you need ranked full text search? If yes, price the search tier honestly and early, before it turns up as a surprise in month four.
- Do you need long-lived connections? They are awkward in both models, expensive in different ways, and worth designing around either way.
- Who is on call? This is the question that actually decides it for small teams, and the one that gets asked last.
The arithmetic underneath is not complicated:
serverless_monthly = requests * price_per_request + storage + egress
server_monthly = instance_hours * rate + storage + egress + ops_fraction * loaded_cost
Set them equal, solve for requests, and you have your crossover point. Most small products are nowhere near it, which is why the default answer for a new product with unknown traffic is the serverless one. Most products with steady, high, predictable load are well past it, which is why mature businesses move back. Both moves are rational and the internet argues about them as if only one can be.
The term people leave out is ops_fraction. Put a real number in it. For a team without a platform engineer, a conventional stack costs one or two days a month in a good month, and a bad month is a bad month.
Where we are honest
The stack described here has never been deployed. The Kubernetes manifests exist, with autoscaling and ingress. Continuous integration runs lint and tests against real Postgres 16 and Redis 7 containers rather than mocks. There is no deployment, no delivery workflow, and no running instance anywhere.
So the floor cost above is a cost we specified and never paid. That is worth admitting, because it is the ordinary failure. It is very easy to design a six-service architecture, write the manifests, feel professional about it, and never commit to the monthly bill or the maintenance that comes with it. The data model in that repository is by far its most finished artefact, and the application code and interface are much thinner than the schema suggests. A well-specified platform with a partial interface is what it is.
The cheapest four-service backend, in the end, is the one that never gets stood up. The second cheapest is the one where somebody added up the floor before the first line of code, and decided on purpose.
The codebase and its state, described plainly: Starterflare.