From one Coolify server to automatic scaling on Hetzner
Coolify deploys to several servers but does not decide how many. What has to move off a single Coolify box before the app can scale, and how an autoscaler fits.
- Author
- Hetscale
- Published
- Last updated
- Tags
- guide, coolify, hetzner, autoscaling
Coolify autoscaling is two problems, not one. Coolify can deploy an application to several servers, but it does not decide how many servers there should be, and it does not create or remove them when load changes; that part is yours. And before either matters, the single Coolify server most people run, with the application, its database and TLS all on one box, has to be taken apart so that a second server is possible at all. This article covers both: the half day to a day of restructuring, and how an autoscaler adds servers once the application server can be copied.
What a single Coolify server looks like
One Hetzner server runs Coolify itself, its Traefik proxy with Let’s Encrypt, the application built from source on that server, a PostgreSQL or MySQL container, often Redis, and a volume for uploads. It is a good setup. It is also exactly one server, and every part of it assumes that.
Adding a second server to it fails for four separate reasons:
- The database is on the box. A copy of the server is a second database with diverging data; a deleted server is a deleted database.
- Uploads and other volumes are on the box. Same problem, smaller blast radius.
- TLS is on the box. Traefik on each server would request its own certificate, and Let’s Encrypt’s HTTP challenge would land on whichever server DNS or a balancer picks.
- Coolify is on the box. A copy of the server would boot a second Coolify, with its own scheduler, backups and notifications, and deleting one of the servers that scale could delete your deploy tool.
The order below fixes them in the order that keeps the site running throughout.
Step 1: move the database and the other state out
Create a second, small Hetzner server and add it to Coolify as a server (Coolify connects over
SSH with its own key). Create the database resource there instead of on the application server,
dump and restore, switch the application’s DATABASE_URL, and verify before dropping the old
container. Or use a managed database provider; Coolify does not care where the database is as
long as the application can reach it.
Redis goes the same way if sessions or cache live in it. Uploads go to S3-compatible object storage (Hetzner Object Storage or any other), which most frameworks support with a driver change and a one-off copy.
When this step is done, the application server holds nothing you would miss. That is the test: if this server disappeared, would anything be lost? Until the answer is “no”, stop here; the rest of the article does not apply, and the preflight asks you to confirm exactly this before anything is copied.
Step 2: move the application off the Coolify host
Add a fresh Hetzner server to Coolify, as in step 1, and deploy the application on it. This new server is the seed: the server an autoscaler copies. Check that it serves before you remove the application from the original box; step 3 moves the traffic to it. Coolify stays where it is; step 4 explains why it must.
An autoscaler that copies the seed needs no registry image. A copy carries the image Coolify built on the seed, so an application built from Git works as it is, and so does a Docker Compose application that keeps no state on the server. A Compose stack with a database or a volume in it does not: that part moves out in step 1. A worker or cron container in the stack runs on every copy.
Step 3: put TLS on a load balancer, not on Traefik
Create a Hetzner Cloud Load Balancer with a health check on your application’s health endpoint, terminate TLS there with a managed certificate, and point DNS at it. The step-by-step version, including how to move the certificate without a gap, is Put your Docker Compose app behind a Hetzner Load Balancer; the Coolify-specific part is the proxy setting on the application servers.
Two ways that work:
- Set the server’s proxy to None and publish the application’s port directly. The balancer talks to that port over the private network.
- Keep Traefik and give the application an
http://FQDN instead ofhttps://. Traefik then routes by host name without requesting a certificate; the balancer terminates TLS in front of it.
Either way, no server in the group requests certificates. The balancer’s health check is now the thing that decides whether a server receives traffic.
Step 4: keep Coolify off the group
Coolify itself stays on a server that is not part of the group: a copy of a server that runs Coolify would boot a second Coolify. The original box is a fine candidate: it was created before Hetzner’s price adjustment of 15 June 2026, so it keeps its old price for as long as it exists, and it already has Coolify installed. Once the application runs on the seed, strip the box down to Coolify.
The application server can now be copied
All state is external, TLS ends at a load balancer, and Coolify runs elsewhere. A copy of the seed now runs the application on its own, which is what an autoscaler needs. Coolify’s own multi-server deployment is the manual way to add servers: it needs a registry image, and you add and attach each server in Coolify yourself.
What Coolify does not do is decide when to add the third server or remove the second. It has no view of load, no notion of a minimum and a maximum, and no health-check-driven replacement. You add servers when you think you need them and remove them when you remember. For a campaign that you know about in advance, that is fine. For a slow rise over months, a broken server at 03:00, or a server type that is suddenly out of stock, it is not.
What an autoscaler adds
A control plane sits beside Coolify and does the capacity part. This is how Hetscale works with Coolify: Coolify keeps deploying to the seed, and Hetscale copies it.
- It creates the server from a snapshot of the seed. From the load balancer’s request metrics and the servers’ CPU, it decides that the group needs one more and creates a Hetzner server of the group’s type in the group’s location from the current release, a snapshot of the seed, with its own ownership labels on it.
- Coolify is left out. Coolify runs a monitoring container, Sentinel, on every server it manages, and on a copy of the seed it would report to Coolify as the seed. The new server boots with one fixed first-boot step that removes that container; the step holds no secret and is the only thing Hetscale runs on the server. Coolify never learns about the copy, so it needs no registry image, no multi-server setup and no Coolify server slot.
- The balancer decides when it is ready. The server becomes a target and counts as capacity only once the load balancer’s health check passes.
- Removal is the same in reverse. Remove the target, wait for open connections to finish, shut the server down, delete it, and never delete a server that does not carry the labels from step 1.
Hetscale asks for no Coolify token and calls no Coolify API. The Hetzner API token lives in one
place, Hetscale; Coolify’s own Hetzner integration is not used. The .env files Coolify writes on
the seed are inside every snapshot, like anything else on its disk; the snapshots stay in your
Hetzner project.
One habit to know about. A Coolify deployment updates the seed, not the copies: new servers boot from the current release, never from Coolify. When a deployment has finished, take a new release on the group’s Deployment tab, which reminds you, then replace the older servers; they leave one at a time, each once its replacement serves.
A second mode is planned. A pull request open upstream (coollabsio/coolify#11799) adds a deploy to a single server to Coolify’s API. Once a Coolify release carries it, groups that meet Coolify’s multi-server rules can use it, and Hetscale will register their new servers in Coolify then.
The honest fit
| Starting point | Prerequisite work | Time | Fit (1–10) |
|---|---|---|---|
| Coolify, single server, app and database together | Steps 1–4 above | Half day to a day | 3, then 7 |
| Coolify, multi-server ready | None | 20–30 minutes | 8 |
The fit numbers are from our onboarding table and are what the preflight tells you before anything is created. A single-server Coolify install is a 3 because the work is real and yours; the same install after the four steps is a 7, and a Coolify setup that already deploys to several servers behind a balancer is an 8, with the tokens, the preflight and one supervised Rehearsal (one short-lived server, billed by Hetzner for at least one started hour) as the whole setup. The two case pages, Coolify on a single server and Coolify, multi-server ready, have the steps and the questions people ask about each.
Whether the group would ever grow past two servers is a question for your traffic, not for this article. The calculator gives a first estimate from your server type, your fleet today and the shape of your traffic; a read-only trial replaces the estimate with your last 30 days of metrics before anything is changed.
Frequently asked questions
Do I lose Coolify? No. Coolify keeps building and deploying your application to the seed, as it does today. Your dashboard, your logs and your environment variables stay where they are. The servers the autoscaler adds are copies of the seed that Coolify never sees.
Does Coolify need my Hetzner API token? No, and the autoscaler needs no Coolify token. It creates the servers from the seed’s snapshot and calls no Coolify API. Coolify’s built-in Hetzner server creation is not used, because it cannot set the labels that make a server safe to delete later.
Can I keep the database in Coolify? Yes, on a server that is not in the group. Coolify manages databases on any of its servers; the only rule is that the database server is never one of the servers that scale.
What about Docker Compose applications in Coolify? They work if the stack keeps no state on the seed. The autoscaler copies the whole server, so every new server runs the same stack: a database or a volume in it moves out first, and a worker or cron container in it runs on every copy.
Which Coolify versions are supported? The autoscaler calls no Coolify API, so there is no API version to match. The path was proven live on Coolify 4.4.3: no copy reported to Coolify, and the seed and the application stayed running in Coolify throughout.