Back to all posts

Published January 15, 2026

Why your Hetzner bill is higher than expected

Piotr HajkowskiBy Piotr Hajkowski

In this article

  1. Check these first
  2. 1. Forgotten or orphaned resources
  3. 2. Billing rules that don't match AWS or Azure habits
  4. 3. Rightsizing drift

Pricing note. All euro amounts below exclude VAT and use Hetzner pricing effective 1 April 2026.

You opened the invoice and saw that the total was higher than it should be. Nothing major had changed. No new services went live. No project doubled in size. And yet the number is wrong, sometimes by a few euros, sometimes by a few hundred.

You are not imagining it, and it is highly unlikely that you are looking at a billing error. Hetzner's metering is straightforward: hourly, prorated against a published rate card. Invoices match the resources in the account. Instead, the account tends to drift away from your expectations, usually in small ways that build up: a few line items that are overlooked, a few billing rules that differ from AWS or Azure, and one or two places outside the Cloud Console where charges quietly accumulate.

This guide is a diagnostic tool. Start with the short list of high-value causes below, then skim through the categories and stop at the ones that apply to you. The final section is a ten-minute reconciliation process that you can perform on the invoice in front of you.


Check these first

If you want the short version, match your symptom to a row and start there.

SymptomLikelihoodUsually meansCheck first
Baseline cost stayed high month after monthCommonA resource still exists (stopped server, detached volume, old snapshots)Review the resource list in the Console or via the hcloud CLI
Fleet-wide baseline drifted upward over monthsCommonRightsizing drift — CCX where CPX or CAX would doReview server types across the fleet and flag anything non-production on CCX
One server line item jumpedCommonResize, backups turned on, or a type changeCompare this month's server types and backup settings against last month
Costs grew after an incident or launchCommonAutoscaler floor, temporary upscale, or blue/green capacity left in placeCheck autoscaler minimums and resize history
Cloud Console looked normal but invoice was higherCommonAnother project, Robot, VAT, or a one-time feeWalk every project in the dropdown, check Robot, and read the invoice tax and fee lines
Traffic charges appeared unexpectedlyPossibleWorkload growth, public-path traffic, or a compromised serverReview per-server outbound traffic and network paths

If none of those fit, the categories below cover the rest.


1. Forgotten or orphaned resources

This is the largest category of surprise charges. Nothing dramatic — just resources that remain after the job that justified them has finished.

Stopped servers still billed at the full rate

Stopping a Hetzner server does nothing to the bill. The hourly rate accrues until the server is deleted, so a CCX13 at €15.99/month costs the same powered off as under production load. This is the single most common reason a monthly baseline is quietly higher than it should be, especially on teams migrating from AWS.

Check: Run hcloud server list and look for status off. Cross-reference against the workloads you expect to be running.

Fix: If you might need the disk later, snapshot it (~€0.57/month for a compressed 40 GB image) and delete the server. A snapshot preserves optionality at 3–5% of the running cost.

Detached volumes from old experiments

Volumes survive the server they were attached to, which makes them useful during migrations and rebuilds, but also easy to forget afterward. At €0.0572/GB/month a detached 100 GB volume is €5.72/month, and five of them from abandoned database experiments and old imports is €28.60/month indefinitely.

Check: Run hcloud volume list and look for an empty SERVER column.

Fix: Copy anything worth keeping to Object Storage (cheaper for cold data) and delete the volume.

Snapshot libraries nobody is pruning

Hetzner snapshots are full, compressed images with no incremental storage, deduplication or automatic retention. Every snapshot taken before a deploy, schema change, or resize stays on the bill at €0.0143/GB/month until someone deletes it. Twenty compressed 40 GB images is about €11.44/month and grows monotonically.

Check: hcloud image list --type snapshot shows every snapshot with its size and age.

Fix: Delete anything older than your actual rollback window (usually 30–90 days); document why anything older survives.


2. Billing rules that don't match AWS or Azure habits

Some invoice surprises are not about forgotten resources. They are correct charges that follow rules your intuition did not predict.

Backups are a flat 20% of the server price

Hetzner bills automatic backups as a monthly surcharge equal to 20% of the server's price, not per-GB like AWS or Azure. That is reasonable for stateful production systems. Enabled as a default across a fleet, it adds a 20% surcharge to CI runners, staging, and ephemeral workers where backups are not useful. A CCX13 adds €3.20/month; a CCX33 adds €12.50/month.

Check: Servers with backups enabled show a backup icon in the Console, and the invoice itemises backups on their own line.

Fix: Disable backups on anything that can be rebuilt from code or upstream data. Keep them on systems where state is expensive to reconstruct.

Included traffic is attached to each server, not shared across the fleet

Traffic allowance is granted per server. That means one egress-heavy node can generate overage even when the rest of the fleet remains well below its own allowance. This is a common surprise when one server handles downloads, API traffic, or media delivery while quieter nodes sit with unused headroom.

Check: Compare outgoing traffic per server and look for a single hotspot rather than assuming the fleet's unused allowance will offset it.

Fix: Move heavy egress to infrastructure designed for it, or change routing so the traffic-heavy role is handled more intentionally.


3. Rightsizing drift

Rarely a dramatic spike. More often the reason the bill drifted upward over months and stayed there.

Dedicated vCPU where shared would do

CCX (dedicated vCPU) is materially more expensive than CPX (shared x86) or CAX (shared Arm) for comparable specs. A CCX13 is €15.99/month; a CPX11 is €5.99; a CAX11 is €4.49. Teams often standardise on CCX for production, which is correct, then inherit that default for staging, CI, internal tools, and low-duty-cycle workers where the dedicated-CPU guarantee is not actually needed.

Check: Look at server types in hcloud server list and ask which are production-critical. The rest are candidates for CPX or CAX.

Fix: Move non-critical workloads to CAX if the stack is Arm-compatible, CPX otherwise. Reserve CCX for services that genuinely need stable CPU performance.

Sized for the backfill, kept for the steady state

A server provisioned for a one-time import, migration, or reindex often stays in place for the much smaller ongoing workload. The backfill was 10–100× the steady state, but the type never got revisited because the ongoing job completes quickly and nothing looks broken. Utilisation drops to 5–10% while the server is charged at full tier.

Check: Compare the server's peak utilisation during the original import against current utilisation — most monitoring tools show the drop clearly.

Fix: Downsize to a tier matching the steady state. For scheduled jobs, consider moving to a short-lived worker provisioned on demand.

Upsized for an incident, never downsized

A resize up to survive a launch spike, DDoS, or load event often becomes permanent once the event passes. A CCX33 instead of a CCX23 is roughly €30/month extra — left in place for a year that is €360 per server, and most fleets carry one or two such cases. What makes this sticky on Hetzner is that disks can be upsized but not downsized: if the resize also increased disk, the only path back is a manual migration. The "CPU and RAM only" rescale option preserves the downgrade path but is easy to miss in the UI.

Check: hcloud server list -o columns=id,name,type,primary_disk_size shows current tiers and disk sizes; compare against what the workload actually needs and against resize history around past incidents.

Fix: When scaling up for a known one-off event, pick "CPU and RAM only" so the disk stays on the smaller tier. If already stuck at a larger disk, schedule the migration explicitly — it will not happen on its own.


4. Autoscaling and automation

Autoscalers, CI systems, and batch workers create infrastructure when no human is looking. Most of the time they behave. The times they do not are hard to see.

Autoscaler minimum raised after an incident, never lowered

During a spike or DDoS someone raises the cluster's minimum instance count to stabilise the system. The incident passes; the floor does not. The cluster permanently holds extra capacity because the floor looks like a deliberate decision to anyone who did not witness the event. Three idle CPX41s sitting at the floor is about €105/month.

Check: Review autoscaler configuration and compare the minimum instance count with pre-incident values.

Fix: Lower the floor back. If there is a real need for more baseline capacity, document the reason so the next review has context.

CI runners that never scale to zero

A self-hosted GitHub Actions or GitLab runner pool sized for peak concurrency is often left at full count overnight, on weekends, and through holidays. The runners report "healthy": no failed jobs, no alerts, so nobody notices. Five idle CPX31s at €11.99/month each is €60/month of pure idle capacity.

Check: Cross-reference runner counts with actual pipeline concurrency, especially outside business hours.

Fix: Configure the pool to scale to zero when idle, or use ephemeral per-job runners for burst-heavy workloads.


5. Traffic and egress

Hetzner's per-TB pricing is forgiving relative to AWS, but "forgiving" is not "zero". The expensive cases usually slip through because the application still works and no operational alarm goes off.

Real workload growth crossing the allowance

A server that stayed inside the 20 TB monthly allowance for years can start exceeding it quietly — an asset goes viral, a build pipeline starts pulling aggressively, an external integration begins scraping. Overage is €1/TB (EU), per server. A server doing 25 TB/month adds €5; 100 TB/month adds €80. No circuit breaker fires — only email notifications at 75% and 100%, which are easy to miss.

Check: hcloud server list -o columns=id,name,outgoing_traffic,included_traffic shows per-server usage against allowance.

Fix: If the growth is real, move large public downloads off the application server to a dedicated delivery layer, such as a CDN or a separate download origin. If the traffic comes from scraping rather than real users, add rate limits before you change the architecture.

A compromised server running at line rate

A server with a public IP has all ports open to the internet unless a Cloud Firewall is explicitly attached. If compromised and joined to a botnet, it can push 500+ Mbit/s sustained without any application-side symptom. The first indication is usually a €100–200 traffic overage on a server that normally uses a few hundred GB per month.

Check: Look for per-server outbound traffic outliers — an application server jumping from 500 MiB/day to 500 GiB/day is the signal. Inspect with ss -tupn and last for unfamiliar connections and logins.

Fix: Isolate the server (deny-all firewall or detach the primary IP), capture a forensic snapshot, rebuild from a known-good image. Attach a minimal-ruleset Cloud Firewall to every server at creation going forward.


6. Invoice presentation

Sometimes the diagnosis is not infrastructure. It is how the invoice is read.

VAT is applied on top of the list price

Hetzner's price list and Cloud Console show net prices; VAT — 19% for German customers or the applicable EU rate — is added at invoice time. A CCX13 listed at €15.99 is €19.03 on a German invoice. Teams that budget from the price list, or that provision via Terraform and never look at the pricing page, can miss a 19–21% gap between the rate card and the invoice total.

Check: The invoice's tax line. Net total × (1 + local VAT rate) should match the gross total.

Fix: Budget in gross terms, or track VAT as a separate line in FinOps reporting so the gap does not keep resurfacing.

Charges from other projects or from Robot

A Hetzner account can hold multiple cloud projects, and the same account can hold Robot-ordered dedicated servers. All land on the same invoice, but only the currently-selected cloud project shows in the Console usage preview. A sandbox, a migration project that was meant to be short-lived, or a departed engineer's scratch project keeps billing invisibly to anyone auditing only the main production project.

Check: Walk through every project in the Cloud Console dropdown, then log into robot.hetzner.com separately to check for dedicated servers and storage boxes.

Fix: Formalise project ownership — every project should have a named owner and a review cadence — and reconcile Robot separately from cloud.

Dedicated server setup fees on first invoices

Cloud servers have no setup fee. Dedicated (Robot) servers generally do, paid once per provisioning. A GEX44 at €252.64/month carries a €314.16 one-time setup fee on its first invoice, making that month effectively €566.80 net. Cycling a dedicated server create-and-terminate style pays the setup fee each time.

Check: The invoice's one-time charges line against any recent dedicated-server provisioning.

Fix: Treat dedicated servers as long-lived fixtures. If you need short-term capacity, cloud servers (with no setup fee) are the right tool.


How to reconcile a Hetzner invoice in 10–15 minutes

When the invoice is in front of you and the question is "which line is the surprise?", a structured reconciliation is faster than hunting. Each step takes 2–3 minutes; most cases resolve before you reach the end.

1. Invoice line-item diff. Open this month's invoice next to last month's. Hetzner breaks down by resource type — cloud servers, Primary IPs, volumes, snapshots, backups, traffic, Robot, one-time fees. The category that grew is the one to investigate first. If a single line grew by more than a few percent, everything below this step is about explaining that one line.

2. Console audit, all projects. Open the project dropdown in the Cloud Console and walk through every project — not just the one you usually look at. In each, skim the server list and the costs summary. You are looking for two things: a project whose existence you had forgotten, and a running baseline that does not match your memory of the workload.

3. Resource inventory check. For each project with workloads, list the five billable object types. Most teams do this via hcloud:

hcloud server list -o columns=id,name,status,type,primary_disk_size
hcloud primary-ip list
hcloud floating-ip list
hcloud volume list
hcloud image list --type snapshot -o columns=id,description,created,image_size

For each row, ask: "Do I know why this exists, and would I create it again today?" Rows where the answer is no are the first candidates for deletion. Pay attention to Primary IPs with AUTO DELETE set to no — those are the unattached-IP charges that quietly keep the baseline up.

4. Traffic outlier check. If the invoice showed a traffic line you did not expect:

hcloud server list -o columns=id,name,outgoing_traffic,included_traffic

One server doing dramatically more outbound traffic than the rest is either a real workload change, a misrouted internal path, or a compromise. All three deserve a closer look.

5. Robot check. Separately, visit robot.hetzner.com and check for dedicated servers, storage boxes, and other Robot-only products. These will not appear in any Cloud Console audit. If there is a one-time fee on the invoice, this is usually where it came from.

6. Terraform cross-check (optional). If the account is managed via Terraform, run terraform state list and diff against hcloud server list. Resources in hcloud but not in Terraform are ad hoc work or drift. Resources in Terraform but not in hcloud are usually a state-file issue, but occasionally mean something was destroyed in the console and Terraform will recreate it on the next apply.

7. Invoice interpretation sanity check. Before concluding the platform misbilled you, verify the VAT line matches the expected rate and no one-time setup fees from recent Robot orders are inflating the current month. Those two account for most "the number is right but not what I expected" cases.

Most invoices resolve in steps 1–3. Step 4 explains traffic-driven surprises, step 5 explains Robot-driven surprises, step 7 explains presentation-driven surprises. The remaining cases, usually the ones that took the longest to find, are almost always an automation pattern from section 4 that requires looking at resource state over time rather than at a snapshot.


Keep this from happening again

Finding a surprise invoice is a one-off triage exercise. Making sure the next invoice does not surprise you is a different problem — it is operational habit. Three small ones catch most of what this article covers:

  • A quarterly resource review across all projects, not just production. Fifteen minutes per quarter prevents most of the §1 (forgotten resources) and §6 (hidden projects, Robot) cases.
  • A default server type for non-production work — CPX or CAX. If CCX is the org default everywhere, rightsizing drift is inevitable. If CCX is an explicit decision, the drift does not start.
  • A named owner on every project. Projects without an owner drift; projects with one tend to get decommissioned when their job ends.

None of this needs tooling. It needs a recurring calendar event and a rule that nobody creates a project without putting their name on it.

If you want the audit continuous rather than quarterly, CloudTally records Hetzner resource state every 15 minutes across every project on an account — including autoscaled servers and overnight CI capacity that only exist when no one is looking, and surfaces these patterns as recurring findings rather than monthly invoice surprises.