Your build nodes sit idle for 19 of every 24 hours, then three developers push at once and the queue blows up for 12 minutes. That is not a misconfiguration — it is the shape of git-push traffic. And it is exactly the shape fixed hardware is worst at.
A git-push PaaS lives on burst. A ten-person team averages maybe 60 to 80 pushes a day, but they do not arrive evenly. They cluster at 9 AM standup, right after lunch, and before the end-of-day deploy. Between those peaks, nothing. Overnight, nothing. The steady-state web traffic your tenant apps serve looks nothing like this, which is why the "just put it on the box you already own" math that wins for app serving quietly loses for the build queue — unless you size the pool deliberately.
Here is what that tradeoff actually costs for a canonical workload — 10 developers, ~70 pushes/day, 4-minute average build (Nixpacks/Dockerfile), Hetzner CPX31 as the builder node (€13.90/month post-June-2026 pricing) — across the three honest sizing strategies:
| Strategy | Builder nodes (fixed) | Monthly hardware | Idle % | p95 queue wait at morning burst (6 concurrent pushes) | Who pays for overflow |
|---|---|---|---|---|---|
| A. Provision for average | 1 × CPX31 | €13.90 | ~35% | 10–14 min | Nobody — developers wait |
| B. Provision for peak | 4 × CPX31 | €55.60 | ~78% | ~30 sec | Nobody — you overprovisioned |
| C. Hybrid (base + elastic burst) | 1 × CPX31 + burst to metered | €13.90 + €8–22 overflow | ~35% on owned | ~1–2 min (with 60-sec autoscale) | GitHub/Depot/Hetzner autoscaled runner, only when needed |
The punchline is in the idle column. Peak provisioning buys you a low queue delay but you pay for four machines to get one machine's utilization. Average provisioning is cheap but your team waits. The hybrid wins on both axes for the workload most small teams actually have — and it is the only one where the math stays sane when burst frequency changes.
The rest of this post shows why the table lands that way, when each strategy genuinely wins, and what Cluster API actually lets you do about it.
Why Build Traffic Is the Wrong Shape for Fixed Hardware
Tenant-app traffic is smooth. Even a small SaaS with 50 requests per second has a diurnal curve you can predict and a baseline that never drops to zero. You can right-size a node pool to that curve and run it at 40–60% utilization without drama.
Build traffic is not that.
A typical 10-developer team on a git-push PaaS (Render, Railway, Coolify, Dokploy, or a self-hosted Bex-style fleet) looks like this over a workday:
| Window (local) | Pushes/hour | Concurrent builds | Builder demand |
|---|---|---|---|
| 00:00–07:00 | ~0 | 0 | Zero — pure idle |
| 07:00–09:00 | 2–3 | 1 | Light |
| 09:00–11:00 | 10–14 | 3–6 | Burst — 4–6× average |
| 11:00–14:00 | 4–6 | 1–2 | Moderate |
| 14:00–17:00 | 8–12 | 3–5 | Second burst |
| 17:00–24:00 | 1–2 | 0–1 | Taper to idle |
Two bursts, each 3 to 6 times the daily average, bookending long idle troughs. The ratio of peak concurrent builds to average concurrent builds is roughly 4:1 to 6:1. That ratio is what hurts fixed hardware: to cover the peak without queueing you must provision for a load that exists maybe 90 minutes a day and eat idle the other 22.5 hours.
Metered builders — GitHub Actions runners, Depot, Vercel's build fleet, or even Hetzner Cloud VMs spun up on demand — are priced for exactly this shape. You pay per minute or per second only when a build is running. Zero builds, zero bill. Fixed Hetzner hardware flips that: you pay for the hour whether a build is running or not. At €0.022 per hour for a dedicated builder VM, the per-minute amortized cost is tiny (€0.00036/min) — about 20 times cheaper than GitHub-hosted runners — but only if the machine is actually building. At 30% utilization, that cost advantage shrinks fast.
This is why the comparison in the opening table is not "Hetzner vs GitHub Actions on sticker price." On sticker price, Hetzner always wins. The question is effective cost at real utilization, plus what you pay in queue delay when you refuse to overprovision.
Strategy A: Provision for Average (One Builder, Developers Wait)
This is the cheapest and simplest fleet: one CPX31 (4 vCPU, 8 GB RAM, 160 GB NVMe) running your build service — BullMQ or RabbitMQ queue, Nixpacks or Docker build, image push, deploy. At €13.90/month it handles the average load comfortably.
What it costs: €13.90/month. Plus essentially nothing else — storage and bandwidth to push images are either negligible or already in the Hetzner allotment.
What it costs in time: Queueing theory is unforgiving here. With one worker and 4-minute builds, a burst of 6 concurrent pushes creates a queue 5 deep. The last push in that burst waits roughly 5 × 4 min = 20 min in the worst case, 10–14 minutes at p95 after accounting for stagger. During the morning burst your team watches spinners.
For many teams this is actually fine. If deploys are not latency-sensitive — preview environments, staging, personal branches — a 10-minute queue is an annoyance, not an outage. If every push is a production deploy that a developer is watching to verify, it is brutal.
Utilization: With ~70 builds × 4 min = 280 build-minutes per day, one node provides 1,440 minutes of capacity. That is ~19% raw utilization over 24 hours, ~35% over the 12-hour workday. Cheap, but you are still idle most of the day — and you still queue at peaks.
When this wins: Small teams (under 5 active pushers), non-urgent deploys, or a team that pushes mostly outside the burst windows. If your p95 wait is under 5 minutes in practice, save the money.
Strategy B: Provision for Peak (Four Builders, Mostly Idle)
To eliminate queueing, provision for the 95th-percentile burst: 4 builder nodes so that 6 concurrent pushes all start within seconds.
What it costs: 4 × €13.90 = €55.60/month. That is 4× the hardware for burst coverage that matters ~90 minutes a day.
Idle math: At 280 build-minutes per day spread across 4 nodes (5,760 node-minutes of capacity), raw utilization is ~5% over 24 hours and ~9% over the workday. Even counting only the 6-hour burst window, peak utilization barely crosses 25%. You are paying for idle capacity 75–80% of the time by design.
When the price hikes matter: Hetzner's June 15, 2026 CPX/CCX repricing roughly doubled these lines. A 4-node peak pool that would have been ~€30/month pre-hike is now €55.60. The same idle waste now costs twice as much. This is where the "own the hardware, it's always cheaper" pitch gets honest: cheaper per compute-minute, yes, but not cheaper per useful compute-minute when utilization is 10%.
What it buys: p95 queue wait drops to ~30 seconds (just scheduling overhead). For a team where every push is a production deploy and "the build started immediately" is a product promise, that latency matters. But you bought it with idle you never get back.
When this wins: Teams where build latency is a product SLA — for example, preview environments where a designer is waiting on a URL — and where the team is large enough that bursts are frequent, not occasional. Above ~150 pushes/day, the idle ratio improves and peak provisioning starts to justify itself.
Strategy C: Hybrid — Fixed Base Plus Elastic Burst
The boring, correct answer for most teams: keep a small fixed base (1–2 owned builders) and burst the overflow to a metered neighbor — GitHub Actions, Depot's managed runners, Blacksmith, or Hetzner Cloud VMs launched on demand via testflows/github-hetzner-runners or similar.
How it works:
- 1 × CPX31 always on (€13.90/month) handles the baseline — the 60–70% of pushes that never contend.
- When queue depth exceeds 1, overflow jobs are dispatched to metered runners and billed per minute.
- When the burst subsides, you are back to one node. No idle fleet.
What overflow costs:
| Metered runner | Per-minute price | 4-min build cost | 1,000 overflow build-min/month |
|---|---|---|---|
| GitHub-hosted (Linux 2-core, post-2026 pricing) | ~$0.008/min (includes $0.002 platform charge) | $0.032 | $8.00 |
| Depot (2 vCPU runner) | $0.004/min | $0.016 | $4.00 |
| Blacksmith (2 vCPU) | $0.004/min | $0.016 | $4.00 |
| Hetzner autoscaled (CX22 ~€0.006/hr effective) | ~$0.00011/min | $0.00044 | $0.44 |
Even at GitHub's sticker price, 1,000 overflow minutes — roughly the entire burst overhead for a 10-dev team — costs $8. That is less than one additional CPX31. At Hetzner-autoscaled runner pricing, it is under a euro.
Blended monthly cost at different scales:
| Team push volume | Build-min/month | Strategy A (1 node) | Strategy B (4 nodes) | Strategy C (1 + burst) |
|---|---|---|---|---|
| 500 min (~35 pushes/wk) | 500 | €13.90 (queues at burst) | €55.60 (78% idle) | €13.90 + ~€2 = €15.90 |
| 1,500 min (~70 pushes/day) | 1,500 | €13.90 (10-min p95 wait) | €55.60 | €13.90 + ~€6–8 = €20–22 |
| 3,000 min (~140 pushes/day) | 3,000 | €13.90 (15-min+ waits) | €55.60 (still 60% idle) | €13.90 + ~€12–16 = €26–30 |
At every tier up to ~3,000 build-minutes, the hybrid is cheaper than peak provisioning by 2× and only modestly more than average provisioning — with queue waits cut from 10+ minutes to 1–2 minutes. The breakeven where "just run 2 fixed nodes" beats hybrid on cost alone is around 4,000–5,000 build-minutes/month, where overflow is no longer occasional but constant.
The per-build nuance nobody puts in the comparison: On fixed hardware, you pay the same whether the build finishes in 2 minutes or 8. On metered runners, a slow build costs proportionally more. If your builds are uncached and take 8–10 minutes (large Docker layers, no registry cache), metered overflow gets expensive faster. Invest in layer caching, Depot's remote cache, or a registry cache on your Hetzner node — it pays for itself in overflow savings.
What Cluster API Actually Lets You Do (and Where It Still Waits)
A Cluster-API-managed fleet on Hetzner (CAPH) has more options than "fixed pool or metered neighbor" suggests, but they come with their own latency.
Cluster Autoscaler (the default): Watches for pending pods, scales a MachineDeployment up, waits for the Hetzner API to provision a VM, boot it, join the cluster, and schedule the build pod. Typical wall-clock from "build queued, no node available" to "build running": 3–4 minutes on CAPH, dominated by Hetzner provisioning and node bootstrap. That is fine for app workloads that scale over minutes, but for a build that itself takes 4 minutes, you doubled the wall time.
Karpenter (where available): Calls the cloud API directly, provisions leaner, often 45–90 seconds to node-ready. Noticeably better for burst, but Karpenter's Hetzner story is thin — the official provider is AWS/GCP/Azure-first, and community Hetzner support is experimental. If you are on CAPH today, you are almost certainly on Cluster Autoscaler.
Scale-from-zero: Both autoscalers support scaling a node pool from zero. That is the cheapest idle — zero nodes, zero cost when no builds are queued — but it adds the same 3–4 minute cold-start penalty for the first build after idle. For overnight idle this is perfect; for the 09:00 burst where three builds arrive within 30 seconds, only the first pays the cold start, the rest queue briefly and then fan out.
Practical fleet shape for a self-hosted PaaS builder pool:
- 1 always-on builder as the base (or 2 if your average load justifies it) — handles the steady trickle, no cold start.
- Autoscaled overflow pool with
minReplicas: 0,maxReplicas: 4, scale-triggered on queue depth or pending pods — handles bursts, scales to zero after. - Optional metered escape hatch — if Hetzner returns
no capacity(as it did for restricted provisioning in June–July 2026) or if the queue still backs up past a threshold, dispatch to GitHub/Depot runners instead of blocking.
This gives you hybrid economics without leaving CAPH: the overflow is still your Hetzner capacity, billed the same per-hour rate, just not running when you do not need it. True metered-per-second bursting to an external provider is the next layer — cheaper cold start, but another vendor in the loop.
Where autoscaling does not close the gap: If your builds are short (2–3 minutes) and your autoscaler is slow (3–4 minutes), autoscaling loses its advantage — the node arrives after the burst is over. For short builds, either keep more fixed base or burst externally to a warm pool. Autoscaling shines when builds are 5+ minutes and the 60–90 second scale-up is a fraction of the build time.
The Decision Matrix
| Your situation | Pick | Why |
|---|---|---|
| Solo dev or 2–3 pushers, <500 build-min/month | A. Average (1 node) | Bursts are rare, queue rarely forms, cheapest by far |
| 5–10 devs, 1,000–2,000 build-min, latency-sensitive previews | C. Hybrid (1 + burst) | Handles both bursts without 78% idle; overflow $4–8/month is noise |
| 10–20 devs, >3,000 build-min, frequent concurrent pushes | C. Hybrid with 2 base nodes + autoscaled overflow | Baseline justifies 2 warm nodes, autoscaler handles the rest |
| 20+ devs, >5,000 build-min, builds = production deploys | B or C with 3–4 base nodes | At this scale idle ratio improves and queue SLA may justify peak |
A few closing notes for the operator planning a fleet:
- Measure your burst ratio before you provision. A week of
git log --since="7 days ago"plus your build durations tells you your actual peak-to-average ratio. Do not guess. - Cache your builds. Remote layer caching (Depot cache, registry cache, BuildKit cache mounts) cuts build time 30–60% and directly cuts both queue delay and metered overflow cost. It is the single highest-leverage optimization in this whole post.
- Do not treat builder nodes like app nodes. App nodes want high, stable utilization. Builder nodes want fast scale-from-zero and low idle. Different autoscaling knobs, different instance types, possibly a separate
MachineDeploymententirely. - Price your idle honestly. The cost of idle is not just euros — it is capacity that could have been app nodes. Every builder node sitting at 10% utilization is a node that is not bin-packing tenant workloads.
The broader lesson generalizes beyond builds: any workload with a high peak-to-average ratio trades poorly against fixed hardware, and build queues are the most bursty workload a PaaS owns. The self-hosted fleet still wins on steady-state tenant traffic — that is where owning the box amortizes beautifully — but for the burst at the front of the pipeline, renting the top of the curve is not a concession. It is the efficient shape.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. The builder-pool sizing above is the same tradeoff Bex navigates: steady app traffic on owned Hetzner capacity, bursty build traffic with room for elastic overflow. Star the repo on GitHub or deploy your first app today.