There's a popular Reddit shape: "I run a SaaS on two Contabo VPSes with Docker Compose and WireGuard between them." The thread fills up fast. The first replies are admiring. Within ten comments, the conversation shifts: "what about backups?" "how do you handle rollback?" "what about HA?" "do you have observability?"
The pattern is real. A two-VPS Compose-and-WireGuard setup is the right shape to start with — cheap, debuggable, no Kubernetes tax. It's also the shape where the operator has to walk a specific maturity arc before the system can handle real traffic, real customers, and real outages.
This guide is the arc in order. What to add first, what to defer, and where the curves bend.
The starting point: two VPS, Compose, WireGuard
The default setup the threads describe:
- Two VPSes, often Contabo or Hetzner.
- App on one VPS, database on the other.
- WireGuard between them for a private network.
- Docker Compose orchestrates each box's services.
- A reverse proxy in front of the app box; the DB box is private.
- SSH access from the operator's workstation.
This is genuinely a defensible architecture for a small SaaS. It's simple. It's debuggable. It scales further than people expect.
It's also missing five things you'll need before you can run it in front of real customers without anxiety.
Stage 1: backups (week one)
Nothing else matters if you can't recover from a bad day. Backups are stage one.
What to back up. The database (full snapshot + WAL or binlog), the volumes that hold user uploads, the secrets that aren't in source control, and the config files that aren't templated from your repo.
Where to back them up. Off the VPS. The whole point is that the VPS could die. Hetzner Storage Box, Backblaze B2, or AWS S3 are all reasonable. Pick one and stick with it.
How often. Database snapshots: daily, with WAL/binlog continuously archived. Volumes: daily or as often as the data changes meaningfully. Configs: on every change, ideally via the repo.
Verify the restore. A backup you've never restored isn't a backup. Spin up a third VPS once and restore the latest backup end-to-end. Verify the app boots against it. Make this a monthly drill.
Encrypt. Backups going off-VPS should be encrypted at rest. restic with a strong passphrase is the easy default; provider-side encryption is fine but doesn't protect you if their key is compromised.
Until all five of these are checked, the two-VPS setup is one bad disk away from a bad story. Get them done first.
Stage 2: rollback discipline (week two)
The first stage protected you from data loss. Stage two protects you from your own deploys.
In a Compose-and-WireGuard setup, rollback is rarely a single command. It's a discipline:
Tag every release. Every image gets a unique tag — usually the git SHA. The image with :latest is also tagged with :<sha>. You always know what was running.
Keep the previous image around. Don't docker image prune the recent images. Keep at least the last three. Disk is cheaper than downtime.
Make rollback a one-command operation. ./rollback.sh <sha> should stop the current container, run the old image, and verify. Don't make the on-call person figure out the command at 2am.
Pre-flight before deploy. The deploy script runs basic checks before swapping containers: image pulled, env vars present, database reachable, current container still healthy. If any fail, deploy aborts.
Verify after deploy. After the new container starts, hit a /version endpoint and confirm the live response shows the new SHA. If it doesn't, the deploy is treated as failed.
This is not the same as zero-downtime deployment. It's the recoverability of a deploy that's about to ship. Get this right before chasing zero-downtime.
Stage 3: visibility (week three)
You can't fix what you can't see. Stage three is making the system observable enough to debug.
Structured logs. Each app log line is JSON: timestamp, level, request ID, route, status, latency. The reverse proxy logs are also structured. Both are forwarded to a central place — Loki, Logtail, or just a file on a backup box.
A small metrics rollup. Every minute, a script reads recent logs and writes rollups: requests by route, errors by route, p50/p95 latency. Stored in SQLite or DuckDB. Thirty days retained.
Event overlays. Deploys, restarts, and migrations emit one-line events into the same store. When you debug a spike, you can see the deploy that preceded it.
A one-page dashboard. Doesn't have to be Grafana. A Flask page that reads the rollups is enough. The point is that one URL answers "what's happening?"
This is the lightweight observability pattern — and it's the right shape for a two-VPS setup. Save SigNoz and OpenTelemetry for when the system has grown past it.
Stage 4: real downtime testing (week four)
Before you can claim HA, you have to know what happens when things fail. Stage four is running the failure cases on purpose.
App VPS dies. What happens? In the bare two-VPS setup, the answer is "site is down." You need to know this with certainty before you can plan around it.
Database VPS dies. The app comes up but can't connect. Most apps handle this badly. Test it now; you don't want to learn at 3am.
WireGuard fails. App can't reach DB. Same shape as DB-down for the app's perspective, but the recovery is different. Test it.
Reverse proxy crashes. All inbound traffic stops. The app is fine; the front door isn't. This is the failure mode the operator usually didn't plan for.
A deploy ships a broken release. Without rollback discipline, this is a real outage. With it, it's a fifteen-second blip.
Running these as drills surfaces the gaps that the architecture would have hidden until they hit you in production.
Stage 5: HA, when needed
Full HA is the most expensive thing on this list. It's worth doing — but only after the previous four stages are solid.
App-layer HA. Two app VPSes behind a small load balancer (HAProxy, Caddy with multiple upstreams, or a managed LB). Stateless app design lets this work. WireGuard mesh now connects all three boxes.
Database HA. This is the hard part. The simple version is a read replica. The full version is automatic failover with something like Patroni for Postgres or MariaDB Galera. Both add real operational burden.
Network HA. If your SaaS is global, you'll eventually want multi-region. This is a bigger change than HA within a region.
Do not skip to this stage. Two-VPS without backups or rollback is more dangerous than two-VPS with backups and rollback. HA without observability means you can't tell when failover happened. The order matters.
What "good" looks like after the arc
After walking the arc, a two-VPS-plus-LB setup with:
- Daily verified offsite encrypted backups.
- Rollback as a one-command operation, verified by version endpoint.
- Structured logs, lightweight metrics, deploy events overlaid.
- Documented failure drills, run quarterly.
- App-layer HA via a small LB.
- Postgres or MySQL with a read replica.
- Status page or at least an external uptime monitor.
…is good enough to run a paying SaaS without dread. It's not "enterprise." It is durable. The customers don't notice the difference.
When the arc doesn't fit
A few situations where the arc above is the wrong choice:
- Heavy compute or specialized hardware. Move to managed earlier; two VPSes won't grow into it.
- Regulated data with strict residency. The off-VPS backup decision gets harder; use a provider that meets the regulation.
- Spiky traffic far beyond steady state. Autoscaling matters; cloud-native is probably the right shape.
- Single-developer side project with no paid users. Do stage 1 only and stop. Don't over-engineer.
Most SaaS that fits the two-VPS shape will benefit from the arc as written.
What the Reddit replies were really asking
When the thread fills with "but what about backups, rollback, HA, observability?", the commenters are pointing at the arc — they just don't always name it.
The arc clarifies the order. Backups before everything. Rollback before HA. Observability before scaling. HA only after the rest. Each stage's investment is bounded; each stage protects the work of the next.
A solo builder running on two VPSes doesn't have to do this overnight. A week per stage gets you through the arc in a month. That month is what turns the cheap-VPS architecture from "impressive on Reddit" to "defensible in front of customers."
The summary
The two-VPS Compose-and-WireGuard architecture is a fine starting point. The maturity arc — backups, rollback, observability, drills, then HA — is what carries it into production. Do the arc in order; do not skip stages. The replies in those Reddit threads are usually pointing at the next stage in the arc you haven't done yet.
