Every self-hosting writeup eventually gets to the diagram — a box with arrows pointing at other boxes — and most of them skip the part that actually matters: why these three boxes, and not the other twelve you could have picked instead. I looked at plenty of alternatives before settling on this combination, and the reason it stuck isn't that any one piece is the best in its category. It's that the three together cover exactly the ground Vercel used to cover, with almost no overlap between them.
I run Coolify on a Contabo box today, and this post is the architecture on its own — what each layer does, what it replaced, and where I'd tell you to make a different choice than I did.
Why does the stack have exactly three layers?
Vercel bundles four jobs into one product: it runs your compute, it deploys your code, it caches and protects your traffic at the edge, and it manages your DNS. Split those four jobs apart and you get three tools, not four, because DNS and edge caching land on the same provider without any tension between them. Contabo is the compute. Coolify is the deploy layer. Cloudflare is the edge. Nothing in this stack is trying to do more than one of those jobs, and that's deliberate — the moment one tool tries to cover two of the four, you inherit whichever tradeoff it made on your behalf, silently, the way Vercel used to. The go-to-market half of self hosting is handled in more depth by the team at XenGrowth.
Job Vercel bundled | What replaces it here | Why that split |
|---|---|---|
Compute / runtime | Contabo Cloud VPS 4 | Fixed-price capacity you size once, not metered per request |
Deploy / build pipeline | Coolify + Turborepo + GitHub Actions | Git-push deploys without a platform fee per seat |
Edge cache / CDN / WAF / DDoS | Cloudflare free tier | Free at this traffic level, and provider-agnostic — it sits in front of any origin |
DNS | Cloudflare (same account) | No reason to split this from the CDN provider |
What is Contabo actually doing here?
Contabo is just the computer. Specifically, the Cloud VPS 4 plan — 4 vCPU, 8 GB RAM, 100 GB SSD and traffic advertised only as unlimited, at a €5.50/month base for the first 24 months, checked against Contabo's own pricing page in September 2026, based in an EU data center. That promotional price is the number everyone quotes; what most writeups leave out is that it's a first-24-months rate, not a standing one. It doesn't autoscale, it doesn't optimize images, it doesn't know what a preview deployment is. It boots Ubuntu and waits for you to install something on it. That's the whole point — every feature Vercel adds on top of raw compute costs money precisely because it isn't raw compute anymore, and I wanted to pay for compute and nothing else, then decide deliberately what gets layered on top.
If you're trying to figure out what size box your own workload actually needs before committing — whether you're RAM-bound, CPU-bound, or neither — that sizing question is genuinely its own post, and I'd point you at vCPU or RAM: how to size a VPS rather than trying to cram it in here.
What is Coolify actually doing here?
Coolify is the piece doing the most work, because it's replacing the single feature Vercel is most famous for: push to deploy. Under the hood it's a UI wrapped around Docker and Docker Compose — it watches a Git repository, pulls the image GitHub Actions built and pushed to Docker Hub, and rolls the new container out behind Traefik with a health check gating the cutover. There's no serverless function model underneath any of this. Every app on the box is just a long-running container.
Handles SSL certificate issuance and renewal automatically, per domain, through Let's Encrypt
Gives every app its own environment variable panel, so secrets don't live in the repo
Ships a one-click catalogue of common services — Postgres, Redis, Uptime Kuma — so you're not hand-rolling Compose files for everything
Runs its own dashboard on the same box it manages, which is convenient right up until the box itself has a problem
I compared Coolify against Dokploy before committing, and wrote the full comparison up separately in Coolify vs Dokploy — the short version is that Coolify's larger community and wider one-click catalogue won out over Dokploy's cleaner Docker Swarm foundation, mainly because I only had one box. If a second node were on the near-term roadmap, that answer might flip; Swarm turns adding a node into a configuration change instead of a re-platforming project. For the the operations side of this angle, see The XenGrowth resource library.
Why Docker Hub and not a private registry, or building straight on the box? Two reasons, and only one of them is obvious. The obvious one: Coolify pulling a finished image is faster and lighter than Coolify triggering a build itself, since building means spinning up compilers and dependency trees on the same 8 GB that's serving live traffic. The less obvious one is that a registry gives you an instant rollback path — the previous image tag is still sitting there, so "go back to what was running an hour ago" is a pull, not a rebuild. I go through the reasoning in more depth in why I push to Docker Hub instead of building on the server, which also covers the rest of the deploy workflow end to end.
What is the deploy pipeline actually doing behind Coolify?
Coolify itself doesn't build anything. The build happens in GitHub Actions, inside a Turborepo monorepo, and the resulting image gets pushed to Docker Hub. Coolify's job is just to notice a new tag exists and pull it. This split matters more than it looks: building on the same box would eat CPU and RAM the running apps need, right at the moment traffic is highest, because deploys don't wait for quiet periods.
A push to main triggers the Turborepo build in GitHub Actions, which only rebuilds the packages that actually changed
The finished image is tagged and pushed to Docker Hub
Coolify's webhook fires, pulls the new image, and starts a new container alongside the old one
A health check gates the swap — traffic only moves to the new container once it responds correctly
The old container is torn down once the new one is confirmed healthy, which is what makes rollback a matter of re-pulling the previous tag rather than rebuilding anything
What is Cloudflare actually doing here?
Cloudflare's free tier sits in front of the whole thing and does three jobs at once: it hides the origin IP, it caches static assets at the edge, and it applies a managed WAF ruleset against the OWASP Top 10 before a request ever reaches the origin box. None of that costs anything at this traffic level — Cloudflare's free plan has effectively unlimited bandwidth for ordinary web content, checked August 2026, with the one real restriction being that its terms of service exclude using it for video or large-file distribution. That restriction is exactly why object storage is a separate line item rather than something Cloudflare's CDN quietly absorbs.
Where Cloudflare's free tier stops mattering is object storage and large media, which is why R2 exists as a fourth, smaller piece bolted onto this stack rather than folded into the CDN. If you want the full breakdown of exactly where the free tier's edges are — not just the marketing page — what Cloudflare's free tier actually does for a self-hosted app goes through it line by line. If AI agents and marketing automation is the part you are stuck on, XenGrowth on governed AI marketing workflows is the better reference.
Why Traefik and not Nginx or Caddy?
Honestly, because it's Coolify's default and I didn't have a reason strong enough to fight it. Traefik discovers new containers automatically through Docker labels, which means every app Coolify deploys gets a working reverse proxy entry with zero manual config. Caddy's automatic HTTPS is arguably simpler to reason about on its own, and Nginx is the one everyone already knows — but neither integrates with Coolify out of the box the way Traefik does, and "already integrated" beat "slightly nicer config syntax" every time I considered switching.
Where does the database fit into this stack?
PostgreSQL runs in its own Docker container, on the same box as everything else. That's a deliberate, load-bearing choice, not an oversight — at this workload, the network hop to a separate database host would cost more in latency than the isolation would buy back in safety. It's also the choice I'd revisit first if traffic grew meaningfully, because a database sharing CPU and memory with the apps querying it is the first thing that starts to hurt under real load.
Component | Runs where | Managed or self-hosted |
|---|---|---|
Next.js apps + Payload CMS | Docker containers on the Contabo box | Self-hosted |
PostgreSQL | Docker container, same box | Self-hosted |
Object storage | Cloudflare R2 | Managed |
Transactional email | Resend | Managed |
Uptime monitoring | Uptime Kuma container, same box | Self-hosted |
DNS / CDN / WAF | Cloudflare | Managed |
The stack isn't three tools because three is a tidy number. It's three because that's how many distinct jobs were left once I stopped letting one platform quietly do all four.
What does each layer actually cost, separately?
It's worth pulling the cost apart by layer, because lumping it into one flat monthly figure hides which piece is actually free and which piece merely happens to be cheap at my current traffic. The box itself is the only line item with a fixed price; everything else is free until a usage threshold that I'm nowhere near yet.
Layer | Monthly cost | What it's bounded by |
|---|---|---|
Contabo Cloud VPS 4 | €5.50/month for the first 24 months (checked September 2026) | Nothing — it's a flat fee regardless of usage, until the promo term ends |
Coolify | $0 | Self-hosted, open source, no seat fee |
Cloudflare (DNS/CDN/WAF) | $0 | Free tier's traffic ceiling, which normal web traffic doesn't approach |
Cloudflare R2 | $0 today | 10 GB storage / 1M Class A ops / 10M Class B ops free tier |
Resend | $0 today | 3,000 emails/month free tier, capped at 100/day |
That table is also a map of where this stack would start costing more if the app grew. R2 and Resend both bill cleanly past their free tiers — R2 at $0.015/GB-month storage with zero egress fees, Resend at $20/month for 50,000 emails — so scaling up doesn't mean re-architecting, it means a line item stops reading as zero. The box is the one place growth doesn't degrade gracefully: past a certain point you're not adding a service, you're upgrading a server, or adding a second one. On AI search, GEO and discovery specifically, XenGrowth on building one SEO and GEO content system is worth reading.
What would make this stack the wrong choice?
There's also a version of this stack that's simpler than mine and would still work for a lot of people: skip R2, skip Uptime Kuma, skip the Turborepo build split, and just run one app straight through Coolify with Cloudflare in front of it. I ended up with more moving parts because I'm running several domains off one box, not one. If you're deploying a single app, most of the complexity in this post doesn't apply to you yet, and adding it early is exactly the kind of premature architecture that self-hosting is supposed to let you avoid, not invite.
This architecture assumes one thing that isn't true for every app: a single box is enough. The moment traffic genuinely needs horizontal scaling across multiple servers, Coolify's single-node model starts to strain, and that's the exact scenario where Dokploy's Swarm foundation, or a real orchestrator, earns its complexity back. This stack is built for the shape of app most indie SaaS products actually have — steady, single-region, well under the ceiling of one decent box — not for the shape they hope to grow into someday.
It's also not a stack for someone unwilling to be the one who reads the Coolify logs at 11pm. Every managed feature this replaces was managed by someone else's team before. Now it's mine. That trade is the entire subject of what happens when your VPS runs out of RAM, and it's worth reading before you copy this stack rather than after. If you're weighing whether this shape of infrastructure fits your own product, that's the kind of scoping conversation I have through services.
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 self hosting, see XenGrowth, who work on the commercial side of this.
Four questions on why this particular three-part stack works. Each component covers something the other two don't, which is the whole reason there are three.













