Coolify on Hetzner: how I host my SaaS products on my own server
My setup for running several Next.js apps on one Hetzner server with Coolify, what it costs, and the deploy pitfalls that each cost me a few hours.
Marcin Fratczak · updated Nov 15, 2026
I run my products on a server I rent, not on a platform that bills per project. Coolify is the piece that makes this bearable: it gives me push-to-deploy, databases, cron jobs and certificates in one panel, on a machine I control.
This page is the setup I actually use and the mistakes I already paid for.
Why not just Vercel
This site is on Vercel. It is mostly static, and for that Vercel is the least work.
The products are different. Each one needs a database, background jobs and file storage. If every side project is its own bill, I start killing projects because of the invoice and not because of the idea. With one server the price is flat: a new project costs me a new folder.
The setup
- One Hetzner server in a European data centre runs everything.
- Coolify v4 on that server, with its built-in Traefik proxy in front of the apps.
- Cloudflare in front of the server: DNS for every domain, proxy on, origin certificates on the server.
- GitHub connected to Coolify. A push to
mainbuilds the app from its Dockerfile and deploys it. - For one product the image is built in GitHub Actions and Coolify only pulls the new tag.
If you want this for one app, the path is: create the app in Coolify, point the domain at the server in Cloudflare, add the environment variables, deploy. Coolify gets the certificate itself once the domain is set on the app.
What cost me hours
A deploy is not git push. It is a handful of steps, and each of them can fail quietly while the site keeps showing the old version and looks healthy.
"Finished" does not mean live
Coolify's finished status means the container started. It does not mean the change is on the site. I do not call a deploy done until I see the change on the live URL.
A push already deploys
git push triggers a deploy by itself. If I start a manual deploy at the same moment, the two collide and mine comes back as failed, although nothing is broken. Now I wait until the app is back to running:healthy and only then ask for another deploy.
Adding an environment variable twice
Through the API, creating a variable that already exists does not overwrite it. Coolify answers 201, so it looks like success, but the container gets two lines with the same name and the later one wins. That is usually the old value.
The symptom is confusing: the app throws a server error right after a change I was sure I had made. Now I check for duplicate keys before I look for the bug in the code.
Variables that exist only at runtime
Three things I keep having to remember:
- Variables are not there while the image is being built. Anything Next.js bakes in at build time needs to be handled on purpose.
- A container reads its variables once, at start. Changing a variable does nothing until the next deploy.
- A variable created through the API is a build-time variable by default and gets a "preview" twin. Build-time secrets end up in the image history, so I switch them to runtime-only and delete the twin.
Wildcard domains and router priority
Two of my products serve customer subdomains, so the proxy has wildcard rules. Traefik picks the router with the highest priority, and Coolify gives its own routers a priority equal to the length of the rule.
Two lessons from that:
- Equal priorities are a coin toss. One product's domain showed another product.
- A wildcard with a high priority swallows every specific domain under it.
Also: after a rolling update Traefik can answer 404 for about ten seconds. I check again before deciding the site is down.
The disk fills up with old images
Every deploy leaves an image behind. When builds start failing for no reason, I look at free disk space first.
Cron jobs that call your own endpoint
Coolify has scheduled tasks per app. If the task calls an endpoint of the app, the endpoint needs a shared secret, and without the secret it should answer 404, not 401. There is no reason to confirm that something lives there.
Would I do it again
If you have one app and no wish to look after a server, stay on a platform. You will pay more and think less.
If you have several products, or you know you will keep starting new ones, your own server with Coolify pays for itself quickly. You just have to accept that you are now the person who gets to find out why the deploy says finished and the site says otherwise.