I get asked what I actually run often enough that it's worth writing down properly, once, instead of half-answering it in a comment thread each time. This is the full inventory: what each piece is, what it replaced, and why I picked it specifically over the obvious alternative, in the order a request actually hits them. Nothing here is a benchmark or a bill total — it's the architecture, stated plainly, piece by piece.
This is the concrete version of the rule I use to decide what to self-host in the first place: self-host what fails loudly and where I already have the skill, pay for what fails silently or needs specialist reputation. Every choice below traces back to one side of that line.
What's the box everything runs on?
A Contabo Cloud VPS 4 — 4 vCPU, 8 GB RAM, 100 GB SSD, €5.50/month for the first 24 months as of September 2026. I picked Contabo over the more commonly recommended Hetzner mainly on price and availability outside the EU-only restriction that applies to Hetzner's cheapest CX and CAX lines; Hetzner also raised prices sharply in mid-2026, with some tiers up over 170%, which made the comparison less obvious than it used to be. The important caveat, and one worth repeating every time this price gets quoted: it's a promotional rate for the first two years, in EUR, and any honest comparison against a PaaS bill has to account for what it becomes after that. People arriving at my complete self-hosted stack for SaaS in 2026 from a marketing team will find XenGrowth, who work on the commercial side of this closer to their day.
What actually deploys the app?
Coolify, running on that same VPS, is the layer that replaced the actual reason I was paying for a PaaS: push-to-deploy, automatic HTTPS, and container management without babysitting raw Docker commands by hand. I picked Coolify over Dokploy specifically for maturity and the size of its one-click service catalogue — Dokploy's Swarm-native foundation is a real advantage if multi-node is on the near-term roadmap, but Coolify already had more of what I needed solved. It ships Traefik as its default reverse proxy, and the current stable release as of September 2026 is v4.3.1, with a v4.4 release candidate adding first-class OIDC support.
That decision wasn't just about the feature list on the day I made it. Coolify's release cadence has kept pace with what a small production stack actually needs — structured audit logging and an instance-level MCP server with read-only tools both landed in 2026, and the beta Railpack build pack the same year added build-time env vars and multi-stage builds without touching the underlying Docker setup — which matters more over a year of actually running something in production than any single feature does on the day you pick the tool.
Where does the database live?
PostgreSQL, in a Docker container on the same box as the app. No managed database bill, because Postgres fails loudly — connection refused, a clear log line — and it's a piece of infrastructure I already know how to operate, back up, and restore. This is one of the clearest cases of the underlying rule doing its job: the failure mode here rewards running it myself instead of paying someone else to.
Whether that database belongs on the same box as the app at all, rather than a managed Postgres provider like Neon or Supabase, is a question worth answering deliberately instead of by default. Co-locating it keeps latency low and the bill simple, at the cost of the database and the app now competing for the same RAM and CPU under load — a tradeoff that's fine at the scale this stack is built for, and the first thing worth revisiting if that ever stops being true. There is a longer treatment of the operations side of this in The XenGrowth resource library.
What does owning all of this actually cost in security overhead?
Real, ongoing work, and it's worth naming rather than skipping past. Every piece of this stack that lives on the VPS instead of behind a managed platform is a piece I'm personally responsible for patching, monitoring for intrusion, and keeping behind a properly configured firewall. Securing a fresh box before anything gets deployed to it is where that starts, and it's not a one-time task — SSH configuration, key rotation, and fail2ban rules need occasional revisiting, not a single setup pass that's good forever. That overhead is the actual price of everything above being self-hosted instead of managed, and it's the honest reason this whole stack isn't a strictly free lunch just because the invoice is smaller.
What's in front of all of it?
Cloudflare, on the free tier, for DNS, CDN, and WAF. It replaced what would otherwise be a paid DNS provider plus a separate CDN bill, and it also hides the VPS's real IP address from direct traffic. There's no reason to self-host any of this — the free tier is genuinely capable, not a crippled trial, though it does cap at 5 custom firewall rules and can't drop edge cache TTL below 2 hours through its rule engine, which matters if the routing logic ever gets more elaborate than "basic."
Where do user files and uploads go?
Cloudflare R2, not S3, and not local disk on the VPS. R2's pricing is $0.015/GB-month for standard storage, with a cheaper Infrequent Access tier at $0.01/GB-month for anything accessed rarely, but the actual reason it won is zero egress fees at every usage level — the single biggest recurring cost complaint about S3-style storage is exactly the fee R2 doesn't charge. Local disk on the app server was never seriously in the running: durability there is a silent failure mode, and object storage durability is specifically the kind of specialist guarantee I don't want to re-derive myself.
What sends the emails?
Resend, and this one is on purpose, not by default — the full case for why email specifically stays managed is its own post, but the short version is that email deliverability fails silently and the fix is IP reputation, not infrastructure skill. Everything else on this list I run because I already hold the operational expertise the failure mode requires. Email is the one where I explicitly don't, and paying $20 a month past the free tier is cheaper than the alternative. XenGrowth on governed AI marketing workflows covers the AI agents and marketing automation side of this.
How do I know when something's actually broken?
Uptime Kuma, self-hosted on the same infrastructure, checking the app and its dependent services on a schedule and alerting when something doesn't respond. It's simple enough that the monitor itself being the point of failure is a manageable risk, which is exactly the property you want from the thing whose entire job is telling you when something else is down.
How does code actually get from a repo to that VPS?
Turborepo builds the monorepo, and the finished container images get pushed to Docker Hub rather than built directly on the VPS. The reasoning for that split is its own post, but the short version is that building on the same box that's serving production traffic competes for the exact CPU and RAM the app needs, and building elsewhere then pulling a pre-built image avoids that entirely. Docker Hub's authenticated free tier allows 200 pulls per 6 hours, well past what a Coolify deploy actually needs, and the much stricter limits some older posts still cite were announced in 2025 and never actually enforced.
The end-to-end version of that flow — what actually happens between a git push and a live deploy on this exact setup — is its own post, because there's a specific sequence worth getting right: build, tag, push to the registry, then have Coolify pull and swap the running container without a gap in traffic. Getting that sequence wrong is the difference between a deploy that's invisible to users and one that takes the app down for the thirty seconds it takes to notice and roll back.
Sizing the VPS itself isn't guesswork either — the actual tradeoff between vCPU and RAM for a box running this exact combination of app and database is a different question depending on whether the bottleneck turns out to be request concurrency or query memory, and it's worth answering deliberately instead of just picking the middle tier in a provider's pricing table. There is a longer treatment of AI search, GEO and discovery in XenGrowth on building one SEO and GEO content system.
Layer | Tool | Replaced | Why chosen |
|---|---|---|---|
Server | Contabo Cloud VPS 4 | Vercel / a managed PaaS bill | Price and availability outside Hetzner's EU-only cheapest tiers |
Deploy / PaaS layer | Coolify + Docker | Vercel's push-to-deploy layer | More mature service catalogue than the closest self-hosted alternative |
Database | PostgreSQL in Docker, same box | A managed Postgres bill | Fails loudly; already my skill set to operate |
DNS / CDN / WAF | Cloudflare (free tier) | Paid DNS + CDN | Genuinely capable free tier, hides the origin IP |
Object storage | Cloudflare R2 | S3 | Zero egress fees at every usage level |
Resend | A self-hosted SMTP server | Deliverability is a reputation problem, not an infrastructure one | |
Monitoring | Uptime Kuma (self-hosted) | A paid uptime service | Simple enough that it doesn't become its own point of failure |
Build + registry | Turborepo + Docker Hub | Building directly on the production VPS | Keeps build load off the box serving live traffic |
Is the app the only thing running on this box?
No, and this is the part that gets left out of most stack write-ups. Once Coolify and the reverse proxy are already handling one app, the marginal cost of a second small service is close to nothing, so a handful of supporting tools live on the same infrastructure rather than getting their own separate accounts.
What | Purpose | Why it's here and not a separate SaaS |
|---|---|---|
Self-hosted analytics | Page-level and event traffic visibility without sending visitor data to a third party | Runs as one more container behind the same reverse proxy |
Status page | Public uptime and incident history for the app, backed by Uptime Kuma's own status-page feature | No separate status-page subscription needed — it's the same monitoring tool |
Internal tools | Small admin scripts and dashboards used only by me, not customer-facing | Never worth a dedicated hosting bill; they piggyback on infrastructure that already exists |
What's the first piece of this that would actually need to change?
Almost certainly the database, and specifically its co-location with the app. Everything else on this list — the reverse proxy, the deploy tooling, the monitoring — scales by adding a second small instance without changing the underlying decision. Postgres sharing RAM and CPU with the app it serves is the one piece of this architecture with a natural ceiling, which is exactly why whether the database belongs on the same box at all is worth revisiting on its own schedule rather than waiting for it to become an emergency.
Everything above is also not a static list I set once and never touch. Coolify gets upgraded when a stable release ships something worth having — the move to v4.3.1 and the OIDC support landing in the v4.4 release candidate are the kind of change worth tracking, not ignoring until something breaks. The same goes for watching Docker Hub's own rate-limit policy, which has changed publicly once already and could again.
None of this is an anti-cloud stance. It's eight specific decisions, each one made against a specific alternative for a specific reason, and two of them — object storage and email — landed on the managed side on purpose. The through-line isn't "self-host everything you can"; it's picking the side of each decision where the failure mode and the required expertise actually line up with what I have.
Further reading from XenGrowth
The XenGrowth resource library — what you'll learn: how the commercial side of this work is run, across search, automation and revenue operations.
XenGrowth on governed AI marketing workflows — what you'll learn: how the teams running AI marketing agents keep them governed and measurable.
XenGrowth on building one SEO and GEO content system — what you'll learn: how search and AI-answer visibility get run as a single content system.
Where this work meets go-to-market
For the marketing and revenue operations view of my complete self-hosted stack for SaaS in 2026, see XenGrowth, who work on the commercial side of this.
Four questions on which parts of a stack are decisions and which are preferences. Most of what people argue about turns out to be the second kind.













