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.

I 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.

Cost

My 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.

The 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.

Control

On 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.

Performance

My 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.

A small Hetzner ARM server gives me dedicated vCPUs and RAM.

DevOps in 2026

Setting 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.

Now 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.

I spend maybe 30 minutes a month on maintenance — OS updates, checking disk space, reviewing logs. My Telegram bot pings me if anything needs attention.

Scaling

A 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.

If 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.

What I Self-Host (and Don’t)

  • My main products and most older projects — Vue frontends, Fastify APIs, PostgreSQL, background workers
  • Automated daily PostgreSQL dumps to object storage
  • Monitoring via Uptime Robot and PostHog

What I don’t self-host:

  • This blog — Jekyll on GitHub Pages, because it’s static and free
  • Email — transactional email goes through a service. Self-hosting email is a deliverability nightmare
  • CDN/edge — Cloudflare sits in front of the server for caching and DDoS protection

Getting Started

I wrote about the specific tools in detail:

The 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.