{
"$type": "site.standard.document",
"description": "Why I self-host my SaaS apps on Hetzner with Kamal: lower hosting costs, more control, local PostgreSQL performance, and simpler deployment in 2026.",
"path": "/why-i-self-host-my-saas-apps/",
"publishedAt": "2026-04-07T04:16:00.000Z",
"site": "at://did:plc:bryys25pc2fnagnyxqgsglhd/site.standard.publication/3mn26bjkkmh23",
"tags": [
"Web",
"Tools"
],
"textContent": "Update, May 21, 2026: I have moved more projects since writing this. My main products and most older projects now run on Hetzner + Kamal. A few projects are still exceptions, but Hetzner is the default now. I also published a newer cost breakdown: Hetzner vs Vercel: What I Pay to Run My SaaS Apps.\n\nI run my SaaS apps on Hetzner with Kamal. I used Heroku years ago, then Render for a couple of years. Both were fine for deployment. I switched to self-hosting for cost, control, and performance.\n\nCOST\n\nMy base Hetzner bill is low enough that I can run multiple apps with PostgreSQL, background workers, and the whole stack on small ARM servers without thinking about per-project hosting cost.\n\nThe cost stays flat. More traffic doesn't change my bill — I'm paying for the server, not per-request or per-seat. If I need more capacity, I bump to the next server tier. Still cheaper than any PaaS.\n\nCONTROL\n\nOn my server, I pick the OS, the Docker version, the PostgreSQL config, the backup schedule, the firewall rules. And I make those decisions once — they're encoded in Terraform and Kamal configs. Spinning up a new project is the same setup, same commands. I'm not dependent on a platform's roadmap or pricing decisions.\n\nPERFORMANCE\n\nMy app and database run on the same machine. Database queries are sub-millisecond — no network hop. On most PaaS setups, the database is a separate service with network overhead on every query.\n\nA small Hetzner ARM server gives me dedicated vCPUs and RAM.\n\nDEVOPS IN 2026\n\nSetting up a server in 2015 meant configuring Nginx, managing SSL certificates by hand, writing systemd service files, and hoping you didn't miss a security update.\n\nNow it's Docker and Kamal. I define my app in a Dockerfile, my infrastructure in Terraform, and my deployment in a Kamal config. One command provisions the server. One command deploys the app. SSL is automatic via Let's Encrypt through kamal-proxy.\n\nI spend maybe 30 minutes a month on maintenance — OS updates, checking disk space, reviewing logs. My Telegram bot pings me if anything needs attention.\n\nSCALING\n\nA single Hetzner ARM server handles far more traffic than most indie SaaS apps will see. PostgreSQL on one server can do thousands of queries per second.\n\nIf I outgrow one server — a great problem to have — Kamal supports multi-server deployments. Move the database to its own box, add a second web server, and kamal-proxy load-balances between them.\n\nWHAT I SELF-HOST (AND DON'T)\n\n * My main products and most older projects — Vue frontends, Fastify APIs, PostgreSQL, background workers\n * Automated daily PostgreSQL dumps to object storage\n * Monitoring via Uptime Robot and PostHog\n\nWhat I don't self-host:\n\n * This blog — Jekyll on GitHub Pages, because it's static and free\n * Email — transactional email goes through a service. Self-hosting email is a deliverability nightmare\n * CDN/edge — Cloudflare sits in front of the server for caching and DDoS protection\n\nGETTING STARTED\n\nI wrote about the specific tools in detail:\n\n * Terraform setup — provision a Hetzner server with four files\n * Kamal deployment — zero-downtime deploys with one command\n\nThe whole thing took about two days the first time. Subsequent projects take an hour or two — and most of that time is setting up API keys for external services, not the hosting itself. After that, every deploy is kamal deploy.",
"title": "Why I Self-Host My SaaS Apps on Hetzner",
"updatedAt": "2026-05-21T00:00:00.000Z"
}