Published November 20, 2025 · Last reviewed April 18, 2026
The Hetzner Cloud costs you're probably missing
In this article
Updated April 2026. All euro figures reflect Hetzner's April 1, 2026 price adjustment. Volumes and snapshots are 30 % more expensive; most compute is up 30-37 %. The audit is more valuable now than when this post was first written, not less.
The typical Hetzner bill is 85 % servers and 15 % everything else. The "everything else" is where the waste lives, and it's almost never the thing an engineer thinks about on a Friday afternoon. A detached volume from a project you retired six months ago. A pair of Primary IPv4 addresses that survived a terraform destroy because they were declared as standalone resources. A development CCX13 that's been "just powered off for the weekend" since September.
None of these appear in the console's monthly total as a line that shouts I am waste. They show up as small, recurring charges that start to feel like fixed cost. This post walks through the six categories where that money actually sits, what each one costs at post-April 2026 rates, and the hcloud commands to surface them. It assumes you already have a Hetzner project and have at least thought about terraform destroy before.
Set up the CLI
Two tools: hcloud and jq. On a Debian/Ubuntu host:
curl -sSLO https://github.com/hetznercloud/cli/releases/latest/download/hcloud-linux-amd64.tar.gz
sudo tar -C /usr/local/bin --no-same-owner -xzf hcloud-linux-amd64.tar.gz hcloud && rm hcloud-linux-amd64.tar.gz
sudo apt-get install -y jqCreate a Read & Write API token in Hetzner Console → Security → API Tokens for the project you want to audit, then point the CLI at it:
hcloud context create audit-2026A quick hcloud datacenter list should print the six Hetzner DCs (fsn1, nbg1, hel1, ash, hil, sin). If that works, you're ready.
Unassigned Primary IPs
Since Hetzner unbundled IP billing in 2022, every Primary IP is a separate billable resource that survives the server it was attached to. Delete a server in the console with "Delete associated IPs" unchecked — the default when clicking through quickly — and the IP stays: €0.50/month, indefinitely.
In a Terraform project the same pattern appears when hcloud_primary_ip is declared as its own resource (the officially recommended pattern) but isn't properly tied to the server's lifecycle. A later terraform destroy -target=hcloud_server.web leaves the IP orphaned in state and on the bill.
hcloud primary-ip list -o columns=id,name,ip,type,assigneeRows with - in the ASSIGNEE column are unassigned. IPv6 Primary IPs are free, so ignore them. For each IPv4 you want to remove, disable protection (if enabled) and delete:
hcloud primary-ip disable-protection <id> delete
hcloud primary-ip delete <id>€0.50/month per IPv4 is small on its own, but five leftover IPs across a project that's been through three architecture reviews is €30/year for nothing.
Floating IPs you never reassigned
Floating IPs cost €3.00/month each whether they're routing traffic or not, and unlike Primary IPs they can never be "attached by default" to a server — they're an HA primitive you have to explicitly assign. The failure mode is predictable: a team wires a Floating IP into a failover plan, the failover plan gets quietly abandoned, and the IP sits there for years.
hcloud floating-ip list -o columns=id,name,ip,type,server,home_locationAnything with - under SERVER is unattached. Before deleting, check whether DNS still points at it — a Floating IP is usually referenced by a hostname somewhere:
hcloud floating-ip describe <name>
dig +short api.example.com # confirm nothing still resolves here
hcloud floating-ip disable-protection <id> delete
hcloud floating-ip delete <id>Each deletion is €36/year. Three abandoned failover IPs across a multi-project org is frequently the single largest finding on a first audit.
Orphaned volumes
Block volumes now cost €0.0572/GB/month (up from €0.044). A 500 GB orphan is €28.60/month, or €343/year, and large orphans are not rare. They're usually created by one of two patterns.
The first is server deleted with "keep volume" checked. The Hetzner console asks this as a checkbox at deletion time and many engineers leave it ticked "to be safe," meaning to come back later. They don't. The second is volume created in the wrong location and never attached. Volumes are pinned to a specific datacenter and can only attach to servers in the same location — an fsn1 volume created by accident when all your servers are in nbg1 is functionally an orphan from the moment it exists.
hcloud volume list -o columns=id,name,size,server,locationVolumes with - under SERVER are orphans. If you suspect recoverable data, attach the volume to any server in the same location and mount it read-only before deciding:
hcloud volume attach <name> --server <any-server-in-same-location>
# on the server:
sudo mount -o ro /dev/disk/by-id/scsi-0HC_Volume_<id> /mntOnce you're satisfied:
hcloud volume detach <name>
hcloud volume delete <name>Snapshot debt
Snapshots are billed on compressed image size at €0.0143/GB/month (up from €0.011) and accumulate the way log files accumulate: silently, and at an increasing rate over time. Two habits drive it.
One is pre-deployment snapshots that never get a retention policy — a CI job snapshots the web server before every deploy, nothing deletes them, and three years later there are 400. The other is "keep this one just in case" snapshots taken manually and forgotten. Both compound: ten 100 GB snapshots at the new rate is €14.30/month.
Find everything older than 90 days:
hcloud image list --type snapshot -o json \
| jq -r '.[] | select((now - (.created | fromdateiso8601)) > (90*24*3600))
| "\(.id)\t\(.name)\t\(.disk_size) GB\t\(.created)"'Delete with:
hcloud image disable-protection <id> delete
hcloud image delete <id>Retention-as-code is the real fix. A nightly job that deletes anything older than N days eliminates this category entirely. For the related "snapshot a server to stop paying for it" pattern, see → Why Hetzner charges for stopped servers.
Backups on servers that don't need them
Automatic backups are a +20 % surcharge on the server's monthly rate. After April 2026 the base rate is higher, so the surcharge is absolutely larger too: a CCX13 at €15.99/month with backups enabled is €19.19/month — €38/year more than the same server without backups.
The surcharge is reasonable for production. It's less reasonable on three years of dev and staging boxes where backups were enabled by default in a Terraform module and nobody went back to prune.
hcloud server list -o columns=id,name,status,type,backup_windowAny row with a BACKUP_WINDOW value has backups on. Turn them off on servers that don't need them:
hcloud server disable-backup <name>Powered-off servers
Hetzner bills powered-off servers at the full rate — the capacity stays reserved on the physical host until you delete the server. This is the single largest category of Hetzner waste we see, and it's the most misunderstood, so it gets its own post: → Why Hetzner charges for stopped servers.
Short version: stopping isn't a billing state. If you want to stop paying for a server, snapshot it and delete it; re-create from the snapshot when you need it back. To find candidates:
hcloud server list -o columns=id,name,status,type | awk '$3=="off"'Traffic overages
Included traffic is 20 TB per server in EU/US regions and 1 TB per server in Singapore, pooled across servers in the same project. Overage is €1/TB in EU/US and €7.40/TB in Singapore — rarely the largest finding, but worth a glance if you run outbound-heavy workloads or a CDN origin on Hetzner.
hcloud server describe <name> -o json \
| jq '{name, included_traffic, outgoing_traffic, ingoing_traffic}'Hetzner emails at 75 % and 100 % of the pool. Treat those emails as a trigger to investigate, not as a first warning — by the time the 100 % email arrives, you're already paying for overage.
Run the whole audit in one go
The commands above are the building blocks. For a one-shot sweep across all six categories plus a dry-run cleanup preview, two scripts live as a GitHub Gist:
hetzner-audit.sh— reports unassigned IPs, floating IPs, orphaned volumes, old snapshots, backup-enabled servers, and powered-off servers, each with a monthly cost estimate.hetzner-cleanup-dry-run.sh— emits thehcloud … deletecommands that would run, without executing them. Copy-paste what you actually want to delete.
Run the audit on a schedule, not once. Orphaned resources accumulate with every refactor and every engineer who leaves; a monthly cron produces more lasting value than a pristine one-time cleanup.
Four errors you'll hit
unauthorized — the token was rotated or belongs to a different project; recreate the context. rate_limit_exceeded — the API allows 3,600 requests/hour/project; add sleep 1 between bulk loops. protected — run hcloud <resource> disable-protection <id> delete before the delete call. locked — an action is already running on that resource; wait 30-60 seconds and retry.
The bottom line
The headline server price is not where Hetzner's waste is hidden. It hides in six categories: primary IPs, floating IPs, orphaned volumes, stale snapshots, unneeded backups and powered-off servers. After the April 2026 price increase, ignoring each one will cost roughly 30 % more than it did a quarter ago.
CloudTally automatically surfaces all six categories on every sync, providing a per-resource cost breakdown equivalent to that produced by the above scripts, so the audit runs itself. Whether you use CloudTally or the Gist, the principle is the same: unused resources are not free, and your bill will keep reminding you of that fact.
More from the blog
Hetzner's June 2026 price increase: how to scale around it
Hetzner's June 15 increase reprices new orders and rescales, not existing servers. How to lock in current rates and route growth around the hike.
Hetzner Cloud billing explained: hourly, monthly, and the invoice
Hetzner Cloud bills hourly with a monthly cap, rounds partial hours up, and invoices in arrears. Here's what actually shows up on your monthly invoice.