The Simplest Production Architecture for a Bootstrapped SaaS
Cloud

The Simplest Production Architecture for a Bootstrapped SaaS

One box, a handful of managed pieces around the edges, and a very short list of things you're not allowed to build yet. Here's the architecture, priced out to $20 a month, and the exact signal that tells you when to add each thing you skipped.

Published September 3, 202611 min readUpdated Sep 6, 2026

Written by · Full-Stack Agentic AI Software Engineer — AI Agents, Automation & Revenue Systems for GTM/RevOps teams

In brief

What's the simplest architecture that can actually run a paying SaaS in production?

One box running your app container, Postgres, and a reverse proxy, with exactly three things pushed off that box: object storage, transactional email, and backups. Everything else people tell you to add on day one — a message queue, a service mesh, Kubernetes, read replicas — solves a problem you don't have yet, and each one has a specific, checkable signal for when you actually do. At Contabo's own promotional list price and Cloudflare and Resend's free tiers, the whole thing runs under $10 a month for its first two years, leaving more than half of a $20 budget unspent.

  • The core is three things on one box: an app container, Postgres, and a reverse proxy in front of both — nothing else needs to run on the machine that serves requests
  • Three things belong off-box from day one regardless of scale: object storage (because container disks aren't durable across redeploys), email (because running your own MTA is a deliverability tax with no upside), and backups (because a backup that lives next to the thing it's backing up isn't a backup)
  • Queues, service mesh, Kubernetes, and read replicas are deliberately deferred, each with a concrete trigger: a background job that needs to survive a crash, more than one service needing to talk to more than one other service, more nodes than one person can patch by hand, or read queries measurably slowing down writes
  • At Contabo's own promotional list price for its first 24 months, a Cloud VPS 4 plus Cloudflare R2 plus Resend's free tier covers this architecture for under €10 (about $10-11) a month, all in, as of September 2026
  • The point of this architecture isn't that it scales forever — it's that it's cheap to outgrow, which is the opposite of what happens when you over-provision early

Evidence notes

Contabo Cloud VPS 4 pricing

4 vCPU, 8 GB RAM, 100 GB SSD for €5.50/month including VAT, promotional for the first 24 months, verified on Contabo's own pricing page rather than a review-site aggregator, checked 7 September 2026.

Contabo traffic policy

Marketed as 'unlimited traffic,' and Contabo's own support documentation now backs that with a fair-use policy rather than a figure: no default bandwidth limit, throttling at its discretion only where usage is 'exceptionally high or disruptive,' notified by email first. It doesn't change the budget below — a bootstrapped SaaS at this stage is nowhere near the kind of sustained load that gets anyone's attention. Checked 7 September 2026.

Cloudflare R2 pricing

$0.015/GB-month standard storage with zero egress fees at any usage level, plus a 10 GB free tier — the reason object storage and backups both land off-box for a few dollars a month, checked September 2026.

Resend free tier

3,000 emails/month, capped at 100/day, no card required — covers transactional email for a pre-revenue SaaS at zero cost, checked September 2026.

Every architecture diagram you'll find for a SaaS that supposedly has zero users looks like it's already been acquired by a company with a platform team. Load balancers fanning out to auto-scaling groups, a message queue between every two boxes, a services layer talking to a services layer. None of it is wrong, exactly — it's just wrong for you, right now, and copying it is how a two-person team ends up spending its runway on Kubernetes YAML instead of the product. The architecture that actually gets a bootstrapped SaaS to its first hundred paying customers is much smaller than that, and it fits on one box.

What actually has to run to serve real traffic?

Three things, and only three: your app, running as a container; Postgres, running as another container next to it; and a reverse proxy in front of both, handling TLS and routing. That's the entire compute layer. If you're running Coolify on top of it, the reverse proxy is Traefik by default and you never touch its config directly — you point a domain at the box, Coolify issues the certificate, and you're done. If you're running plain Compose, Caddy does the same job in about fifteen lines. Either way, the reverse proxy's job is boring on purpose: terminate TLS, forward the request, don't get creative. If you are scoping the simplest production architecture for a bootstrapped SaaS for a business rather than a codebase, XenGrowth's revenue operations work covers that angle.

This is the part people skip past to get to the interesting stuff, so it's worth saying plainly: a single well-specified VPS running these three containers can serve a genuinely meaningful amount of traffic for a SaaS that isn't a consumer app with millions of daily actives. Postgres on modest hardware handles thousands of transactions per second before it's the bottleneck. Your app container is very likely I/O-bound on the database, not CPU-bound on the box. The ceiling here is much higher than it looks from the outside, and almost nobody building a B2B SaaS actually hits it in year one.

What has to live off the box, and why each one specifically

  • Object storage — user uploads, generated PDFs, anything that isn't in Postgres — goes to Cloudflare R2, not the container's own disk. A container's filesystem isn't durable across a redeploy unless you've explicitly mounted a volume for it, and even then, a single disk is a single point of failure for the one class of data that's genuinely irreplaceable: what your users uploaded.

  • Transactional email goes to Resend, not a self-hosted mail server. Running your own MTA means owning SPF, DKIM, DMARC, and a sender reputation you have to protect from every other tenant on the same IP range if you're on a cheap VPS provider — a full-time job disguised as a Docker container. Resend's free tier covers 3,000 emails a month, capped at 100 a day, which is enough for a pre-revenue product's signup and password-reset flow with room to spare.

  • Backups go to object storage in a different provider account than the one running your app, on a schedule that runs whether or not you remember to trigger it. This is the one that gets skipped longest and hurts worst when it does — a backup sitting on the same disk as the database it's backing up survives every failure except the one that actually happens, which is the disk dying.

What you deliberately don't build yet, and the signal that tells you it's time

What you're skipping

Why it's not there yet

The signal that tells you to add it

A message queue

Everything your app does today can happen synchronously inside the request, or as a cron job that runs against Postgres directly

You have a background task that must survive the process crashing mid-run — not "might be nice to retry," but a real correctness requirement

A service mesh

You have one service. Mesh solves service-to-service traffic policy, retries, and observability across many services talking to many services

You have more than two services that need to discover and call each other, and you're already hand-rolling retry logic between them

Kubernetes

One box is easier to reason about, patch, and debug than a cluster, and a cluster's baseline operational cost is fixed whether or not you use it

You need workloads to schedule across more nodes than one person can patch by hand, with independent scaling per service

Read replicas

A single Postgres instance on decent hardware handles read load that would surprise most people, and replication adds its own failure modes to manage

Read queries are measurably slowing down writes on the primary, not "reads feel like they could be faster"

Notice the shape of every signal in that table: it's something you can point to happening, not something you're forecasting might happen. "We might need to scale eventually" is not a signal. A slow query log showing writes queuing behind reads is. On the operations side of this specifically, The XenGrowth resource library is worth reading.

A $20/month architecture, priced out

Piece

Choice

Monthly cost (Sept 2026, Contabo's own pricing page)

Compute (app + Postgres + proxy)

Contabo Cloud VPS 4 — 4 vCPU, 8 GB RAM, 100 GB SSD

€5.50 (promotional, first 24 months)

Object storage + backups

Cloudflare R2, standard tier, first 10 GB free

$0.015/GB beyond 10 GB — roughly $1-2 at this scale

Transactional email

Resend free tier — 3,000/month, 100/day cap

$0.00

DNS, CDN, WAF

Cloudflare free plan

$0.00

Total


roughly €6.50-7.50, about $7-8/month at current exchange rates

That leaves more than half of a $20 budget completely unspent, which is worth sitting with for a second: the constraint on this architecture was never money. It's discipline about what you refuse to add. Two honest caveats on that number: it's in euros, converting to a dollar figure at whatever the exchange rate happens to be rather than a fixed price, and it's promotional for Contabo's first 24 months — worth knowing before you build a three-year cost model on it, not a reason to avoid the plan. If you want headroom before you need it, stepping up to Contabo's Cloud VPS 6 at €7.50/month (6 vCPU, 12 GB RAM, 200 GB SSD) still lands the whole stack comfortably under $20, with real RAM headroom before any of the deferred pieces above become relevant.

Do you need a whole observability stack to run this responsibly?

No, and this is another place people over-build early. A single box doesn't need a distributed tracing setup, a metrics warehouse, and a dedicated on-call rotation — it needs to answer three questions when something's wrong: is the app responding, is the disk filling up, and did the last backup actually succeed. All three are answerable with tools that already exist on any Linux box, plus a health-check endpoint your reverse proxy hits on a schedule. There is a longer treatment of AI agents and marketing automation in XenGrowth on governed AI marketing workflows.

  • An uptime check hitting a real health endpoint (one that pings Postgres, not just returns 200) from outside the box, so you find out about downtime from a monitor instead of a support ticket

  • Disk usage alerting at something like 80% full — a Postgres box that silently fills its disk with WAL segments or logs doesn't degrade gracefully, it stops accepting writes

  • A notification, even a simple one, when the nightly backup job's exit code isn't zero — the worst version of a backup failure is one nobody notices for three months

  • Basic log retention on the app and Postgres containers, rotated so a runaway error loop can't fill the disk on its own

That's the whole list. It's a cron job, a health check, and an alert channel — not a Prometheus and Grafana stack, and definitely not a reason to add a fourth container class to a box that's supposed to stay boring.

Where this breaks first, honestly

One box has exactly one failure mode that matters: the box goes away, and everything on it goes away with it, for however long it takes to bring up a replacement. That's the real tradeoff, and pretending otherwise would undercut the whole argument. What makes it acceptable is that the two things you actually can't regenerate — user data and uploaded files — are the two things this architecture already keeps off the box, in Postgres with WAL-backed backups and in R2 respectively. Losing the VPS itself is an inconvenience you fix by redeploying from the same Compose file onto a new box; losing your only copy of the database is the failure that actually ends a company, and this architecture is built specifically so that one can't happen quietly.

The architecture isn't simple because simple is a virtue. It's simple because every piece you add before you need it is a piece someone has to keep working, forever, for a problem that doesn't exist yet.

What if the app itself needs more than one instance?

This is a narrower question than "do I need Kubernetes," and it's worth separating the two. Running two or three replicas of the app container behind the same reverse proxy — for redundancy during a deploy, or to use more of the box's own CPU — doesn't require a scheduler or a second server. Compose and Coolify both support declaring more than one replica of a service on the same box, with the reverse proxy load-balancing across them. That's still firmly inside this architecture, not a step beyond it: Postgres is still one instance, the box is still one box, and nothing about the deferred list above becomes relevant just because the app tier has a few copies of itself running side by side. On AI search, GEO and discovery specifically, XenGrowth on building one SEO and GEO content system is worth reading.

Where it does become a step beyond this architecture is the moment those replicas need to live on more than one physical box — because now you have a scheduling decision to make about which box runs what, and that's a different problem than the one this post is solving. Until that's true, more app replicas on the same VPS is a config change, not an architecture change.

So what do you actually build, in order?

  1. Provision one VPS and put a reverse proxy in front of it — Traefik through Coolify, or Caddy directly, either is fine, the point is TLS and routing handled once, correctly

  2. Run Postgres as a container with a named volume, not the default anonymous one — this single detail is the difference between data surviving a container recreation and quietly not

  3. Point object storage at Cloudflare R2 from day one, even before you have real users uploading anything, so the code path never touches local disk

  4. Wire transactional email through Resend's API on day one — there's no version of this where self-hosting email pays off earlier

  5. Set up an automated, off-site backup job against Postgres before you have data you can't afford to lose, not after

  6. Everything else on the deferred list stays deferred until its specific signal shows up in a log, a query plan, or an actual incident — not a planning meeting

This is also the reason the database deserves its own post rather than a paragraph here — the anonymous-volume trap alone has cost people their entire dataset, and tuning how I self-host PostgreSQL for my SaaS matters more than which VPS you picked. And if the question you're actually sitting with is the Kubernetes-shaped one, do you really need Kubernetes for a small SaaS walks through that threshold directly rather than as a single row in a table. Where this architecture fits against the wider tooling landscape — Compose, Coolify, or Kubernetes — is its own three-way argument, made in full here.

Further reading from XenGrowth

Where this work meets go-to-market

If the simplest production architecture for a bootstrapped SaaS is part of a growth programme rather than a standalone build, XenGrowth's work on go-to-market systems is the companion reading.

What earns its place in production?

Four questions on the components people add early and rarely need. Each one is a real tool solving a real problem — the question is whether it's your problem yet.

1 / 4
When does a background job queue stop being optional?

Apply this article

How to turn insights into execution

A practical sequence for teams turning concepts into production outcomes.

Self-HostingSaaS ArchitectureDocker ComposeCoolifyVPSPostgreSQLcloud

Audit your current state

Map the bottlenecks and constraints connected to the article’s core problem.

Choose one bounded change

Test the most useful recommendation on one workflow before widening the scope.

Measure what changed

Keep the parts that improve the work, document what failed, and make the next decision from evidence.

Next step

Need help applying this in your stack?

I can translate these patterns into a concrete implementation plan for your team.

Discuss implementationBack to blog

Replies usually within 24 hours.

Next Steps

Continue reading

Should Your Database Live on the Same VPS as Your App?

The pitch for co-location is real: no network hop, no egress bill, one box to back up. So is the failure mode — one OOM event takes the app and the database down together, because they were never separate to begin with.

Navigate

How I Self-Host PostgreSQL for My SaaS (and When I Wouldn't)

Running Postgres in a container is easy. Running it in a way that survives a redeploy, a full disk, and an eventual major-version upgrade is the actual job. Here's the setup, tuned against Postgres's own defaults, and the honest list of where managed wins outright.

Navigate

The Backup Strategy Every Self-Hosted SaaS Needs (3-2-1, Applied)

3-2-1 is easy to nod along to and easy to get wrong in the specific way that only shows up on the day you need it. Here's what it actually means for one VPS running Postgres and Docker volumes, not the generic version you've already skimmed past twice.

Navigate

My Complete Self-Hosted Stack for SaaS in 2026

Contabo, Coolify, Docker, Cloudflare, Postgres, R2, Resend, Uptime Kuma, Turborepo and Docker Hub. Here's every piece of the stack I actually run, what each one replaced, and why I picked it over the alternatives.

Navigate

Moving a SaaS Off Vercel to a €5.50 VPS: What the Numbers Actually Look Like

Nobody publishes their real Vercel invoice, so most migration posts trade in vibes instead of arithmetic. This one builds the comparison from Vercel's and Contabo's own published rates, states every assumption out loud, and shows exactly where the two lines cross.

Navigate

My Free Monitoring Stack for Self-Hosted Apps

Vercel gives you monitoring whether you ask for it or not. A VPS gives you a blank terminal and the assumption you'll figure it out. Here's what to actually watch on a self-hosted box, with tools that cost nothing, and why watching from the box itself is the one setup that will lie to you.

Navigate

Best VPS Providers for Coolify: Contabo, Hetzner and the 2026 Price Shift

Every 'best VPS for Coolify' post still says Hetzner is the cheap option. That stopped being reliably true in June 2026 for two of its most popular tiers, and nobody's updated the recommendation yet.

Navigate

The 80/20 Self-Hosting Stack for Indie Hackers

Most self-hosting advice is all-or-nothing: run everything yourself, or don't bother. Neither is right. Here's the specific 20% of self-hosting effort that actually delivers 80% of the savings, and the parts not worth touching.

Navigate

Coolify vs Dokploy: Which Self-Hosted PaaS Should You Run in 2026?

I run Coolify on my own box and I'd still tell most people starting today to look hard at Dokploy first. Here's the actual decision, not a feature-for-feature tie.

Navigate

The Contabo + Coolify + Cloudflare Stack That Replaced My Vercel Bill

Three pieces, not thirty. Here's exactly what each one does, why that specific combination and not one of the dozen others I considered, and which parts of Vercel each one is actually standing in for.

Navigate