Fly.io to Hetzner + Coolify: How I Cut My Bill by 80%
I moved gazar.dev off Fly.io to a self-hosted Hetzner CPX42 running Coolify, cut my monthly bill to a fifth, and ended up with a faster, leaner setup.
17 May 2026

For the last couple of years gazar.dev lived on Fly.io. It was fine. I never thought about it much. Then I sat down one Saturday morning, opened the billing page, added up what Fly plus Sentry plus a couple of small managed bits were costing me every month, and realised I was paying a serious chunk of money to host a static-ish site, an error tracker, and the kind of side projects that should cost almost nothing. So I bought a Hetzner box, installed Coolify, and moved everything across in a weekend. My monthly bill is now roughly a fifth of what it was. The site is faster. And I have a lot more room to play.
This is the story of that migration, what the new setup looks like, and where the trade-offs actually live.
Why I left Fly.io
Fly.io is a good product. The push-to-deploy workflow is genuinely nice. The dashboard is clean. The CLI is well thought out. When I first put gazar.dev on Fly, I was happy with the choice and I would still recommend it to someone who wants to ship something without thinking about a server.
What changed is that I started adding things. An error tracker. A small backend for a parenting project. A Postgres instance with pgvector. A monitoring agent. Each of those things on its own was cheap. Together they were not. And every new thing I wanted to try meant either spinning up another Fly app and paying for it, or signing up for yet another SaaS with its own bill.
The other thing that nudged me was performance. The site is mostly prerendered HTML served by nginx, so it should feel instant. On Fly with the default region setup it did not, quite. Cold starts on small machines, the occasional regional weirdness, and a TTFB that was always a bit slower than I wanted. None of it was bad. It just kept being slightly off.
The real trigger was when I priced out adding self-hosted monitoring and error tracking properly on Fly. The numbers got silly fast. That was the point I started looking at a single box that could run everything.
The new box
I ended up with a Hetzner CPX42 in Falkenstein, Germany.
- 8 vCPU
- 16 GB RAM
- 320 GB SSD
- Unmetered traffic in practice (20 TB included)
- €37.31 per month including backups and an IPv4 address
The previous bill, across Fly plus the SaaS pieces I was replacing, was somewhere north of €180. Hence "a fifth."
Hetzner is not glamorous. The dashboard looks like it was designed in 2014. The marketing is non-existent. But the hardware is real, the network is fast, and Falkenstein is close enough to most of my readers in Europe and the UK that latency is noticeably better than the Fly region I had been on. You order a box, it boots in under a minute, you SSH in, and you have a real Linux machine.
The mental shift is that you go from "PaaS with a generous free tier and aggressive pricing past it" to "one server that does whatever you want, and the only ceiling is what fits in 16 GB of RAM." For my workload that ceiling is very far away.
Coolify is the part that makes this practical
If I had to manage docker-compose files by hand on a server, I would not have made this move. The reason this works is Coolify. It is an open-source self-hosted PaaS, the kind of thing people compare to Heroku or Vercel, except it runs on your own machine and you control everything.
You install it with a one-line script. It gives you a web UI on port 8000. From there you connect a GitHub App, point it at a repository, pick a branch and a build method (Dockerfile, Nixpacks, or a compose file), set a domain, and it handles the rest: build, deploy, reverse proxy, TLS certificates from Let's Encrypt, automatic rebuilds on push.
For gazar.dev specifically, the deploy pipeline is now:
- Push to
mainon GitHub. - A GitHub Actions workflow runs tests and typecheck.
- The same workflow fires a
GET /api/v1/deploy?uuid=...request at the Coolify API on my server. - Coolify pulls the repo, runs
docker buildagainst my multi-stage Dockerfile, and swaps the running container over. (If you have not touched Docker in anger before, my notes on working with Docker cover the bits I wish I had known earlier.)
The Actions workflow is short:
- name: Trigger Coolify deploy
run: |
curl -X GET \
"${{ secrets.COOLIFY_URL }}/api/v1/deploy?uuid=${{ secrets.COOLIFY_APP_UUID }}" \
-H "Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}"
That is it. No registry to push to, no Fly token to rotate, no flyctl to keep in sync. Coolify's reverse proxy (it uses Traefik under the hood) handles gazar.dev and www.gazar.dev with TLS automatically. If you want a refresher on what is actually happening during that handshake, I wrote up how TLS and HTTPS work in practice.
The first time I watched a build finish and the site come back up on the new server, I genuinely sat there for a second wondering if it was really that easy.
Everything else I now run on the same box
Once the site itself was on the new server, the obvious thing to do was move every other small thing onto it too. Coolify lets you add custom docker-compose stacks alongside your apps, so the ecosystem grew quickly:
- GlitchTip for error tracking. This replaced Sentry SaaS. GlitchTip is Sentry-compatible at the SDK level, so the only change in the frontend was the DSN. I now get error tracking with zero extra spend.
- Beszel for server and container metrics. The hub runs in a Coolify-managed compose stack and reads from a host-mode agent on the same machine. Pretty dashboards, free.
- A Postgres instance with pgvector, which Coolify provisions as a one-click database service. It feeds a parenting side project that needs embeddings. On Fly, that would have been another paid app plus a managed pgvector add-on.
- The Coolify-managed Traefik proxy that fronts all of the above with TLS.
The point is not that any one of these tools is special. The point is that adding the next one is essentially free. I do not have to think about whether a new toy is worth a monthly fee. If it fits in RAM, it runs. That changes how I experiment.
The speed difference
I did not expect a big speed jump. The site is mostly static HTML and the bottleneck on a small site is usually TLS handshake plus the network, not the server. But Falkenstein is closer to my readers than the Fly region I was using, and the box is genuinely beefy compared to the shared Fly machines I was on. TTFB from London dropped from "fine" to "actually noticeably fast." Lighthouse scores went up slightly. Builds on the server itself are quick because 8 cores and 16 GB give Docker plenty of room.
The clearest perf win was actually for the backend bits, not the site. The parenting backend on Fly was on a shared-cpu-1x with 256 MB of RAM, and it spent most of its life either swapping or cold-starting. On the new box it just runs. No cold starts, no swap, no waiting.
What you give up
I want to be honest about the trade-offs because there genuinely are some.
You give up multi-region. Fly's whole pitch is that it is easy to put your app close to your users in multiple places. A single Hetzner box in Falkenstein is one region. For a global SaaS that matters. For a personal site and a handful of side projects, it does not. Cloudflare in front of the box handles most of what multi-region would have given me, in terms of edge caching.
You give up "they will fix it at 3am." If the machine falls over, that is on me. In practice it has not fallen over, and Hetzner gives you out-of-band console access if it does. But the comfort blanket of a managed platform is real and you should price it in honestly when you compare.
You give up some of the polish. Fly's CLI and dashboard are nicer than Coolify's. Coolify is good and getting better fast, but there are still rough edges, the kind where a button does not quite do what you expected and you go SSH in to fix it. None of these have cost me anything more than ten minutes so far, but they exist.
You take on the responsibility of patching the OS. I set up unattended-upgrades and Hetzner snapshots and moved on, but it is a thing you have to remember to think about every few months.
What I would tell someone considering the same move
If you are paying more than about €50 a month across a PaaS plus a handful of small SaaS tools, and you are comfortable with ssh and docker compose, the maths is on the side of moving. A CPX31 at €15 a month is enough for a lot of people. The CPX42 I picked is overkill for gazar.dev alone and was sized for everything else I wanted to pile on.
What you want before pulling the trigger is a clear inventory of what you are paying for and what you would have to self-host. For me it was: web hosting, error tracking, monitoring, a small Postgres, and a couple of background worker apps. Every one of those has a free, mature, self-hostable equivalent that Coolify will run for you.
What you do not want is to do this for a service that real customers depend on without also setting up backups, alerting, and a recovery plan. The cost saving is not worth a Saturday outage you cannot diagnose because you have not built the muscle yet. Start with the personal stuff. Get used to the box. Then move the things that matter.
A year ago I would have told you that the PaaS premium was worth it for the convenience. I still think it is, for some people. But the gap between "managed platform" and "your own box with Coolify on it" has gotten narrow enough that for a single-person operation with a handful of small services, the cheaper, more flexible side of that gap is where I want to be.
I get my Saturday mornings back, my bill is a fifth of what it was, and the next time I want to spin up a tool I do not need anyone's permission.
If you enjoyed this, I write a weekly newsletter for senior engineers thinking about architecture, infrastructure, and the move toward staff and principal roles. You can subscribe here.