Skip to main content

Two Hetzner API Changes, One November 2 Deadline: A Fleet Operator's Migration Checklist

8 min readDora NodaDora Noda
Share
On this page

Hetzner's Cloud API changelog published two entries 24 hours apart in September — one that gives your automation slack, and one that takes something away on November 2. The first lets you delete an Object Storage bucket with up to 1,000 leftover multipart-upload parts still in it. The second deletes the legacy deprecated response field on Images, Server Types, and Load Balancer Types, and anything still reading it breaks — not loudly at compile time, but silently at runtime, when the field simply stops arriving.

If you run a Cluster API fleet on Hetzner, or any controller, cleanup script, or dashboard that talks to the Cloud API, you have 19 days left on the second one. This post is the migration checklist: what each change does, the exact old-to-new field map, the exceptions that will still bite, and why this is a standing runbook item rather than a one-off chore.

The two changes in 60 seconds​

#ChangeDateWhat to do
1deprecated response field removed on Images, Server Types, Load Balancer TypesNovember 2, 2026Read deprecation instead (locations[].deprecation for Server Types); null means not deprecated
2Bucket delete tolerates up to 1,000 leftover in-progress-or-aborted multipart partsLive since September 9, 2026Simplify bucket-teardown scripts, but keep emptying real objects — and whole-project deletes still need fully empty buckets

That is the whole post in one table. The rest is the detail that keeps the migration from half-working.

Change 1: the deprecated field dies November 2​

The September 8 changelog entry is short: the response field deprecated on every endpoint returning Images, Server Types, and Load Balancer Types disappears on November 2, 2026. Its replacement is the deprecation object, which the changelog describes as answering if, when, and how a resource is deprecated. If the field is null, the resource is not deprecated.

The object itself carries two timestamps: announced (when the deprecation was published) and unavailable_after (when the resource can no longer be used in new resources — for an image, the date after which no new server can be created from it). This shape is not new. ISOs went through the identical migration years ago, Load Balancer Types grew their deprecation field on June 5, 2026, and Server Types have carried per-location deprecation info since September 2025. November 2 is just the day the old field finally stops being sent.

The migration map, per resource:

ResourceStop readingStart reading
Imagesdeprecatedtop-level deprecation (null = fine)
Server Typestop-level deprecatedlocations[] entry for your location, then its deprecation
Load Balancer Typesdeprecated (a date string)top-level deprecation

Server Types is the row that will catch people. Deprecation there is per location — a type can be deprecated in Falkenstein while still orderable in Helsinki — so there is no single boolean to check anymore. Any code shaped like "is this server type deprecated" must become "is this server type deprecated in the location I am about to provision in." The official SDKs have been pointing this way for a year: hcloud-go marked ServerType.DeprecatableResource deprecated in favor of ServerType.Locations, and now ships exp/deprecationutil helpers (ImageMessage, and ServerTypeMessage taking an explicit location name) that encode exactly the per-location read. Downstream projects are already mid-migration — Mondoo's cnquery, for example, moved its Hetzner provider from the legacy string to a deprecation dict carrying both timestamps.

The failure mode deserves emphasis because it is quiet. A removed JSON field does not break typed clients at build time: the struct field stays, the compiler stays happy, and the value just arrives as the zero value forever. A dashboard checking the old field will report every image and server type as not-deprecated from November 2 onward — not with an error, but with a confident, wrong green.

The dangerous consumer is not the script that crashes; it is the one that keeps running and stops telling the truth. Grep for deprecated (not deprecation) across every controller, operator, Terraform module, and dashboard query before the deadline, and treat each hit as a bug until proven otherwise.

For CAPH fleets specifically, there is a related reason to care about server-type churn beyond this field removal. CAPH stopped validating hcloudMachine.spec.type in v1.0.7 precisely because deprecated types keep rotating out from under static validation — not all types exist in all locations, and the set moves. The provider pushed that responsibility to the operator. Reading per-location deprecation data in your own preflight checks is how you pick it back up.

Change 2: bucket deletes forgive 1,000 leftover multipart parts​

The September 9 entry relaxes a rule: a bucket can now be deleted even if it still holds up to 1,000 leftover parts from multipart uploads that are in progress or have been aborted. Previously the bucket had to be completely empty, and multipart debris — invisible in normal object listings, still occupying space — was the classic reason a "delete this bucket" step failed in cleanup automation for no apparent reason.

This is a genuine simplification for lifecycle scripts. If your tenant-offboarding or CI-teardown flow lists and aborts multipart uploads before deleting a bucket, that sweep is now belt-and-suspenders for the common case rather than load-bearing. Note the threshold, though: up to 1,000 parts. A workload that churns large multipart uploads and aborts them carelessly can still exceed it, so keep the sweep for heavy-upload tenants and treat the new tolerance as forgiveness for stragglers, not a license to leak parts.

Two exceptions still bite, and both are spelled out in the entry:

  1. Real objects of any kind must still be removed first — including locked or versioned ones, per the bucket-deletion FAQ. The 1,000-part tolerance covers multipart debris only.
  2. Whole-project deletion still requires fully empty buckets. If your automation tears down an entire project, every bucket in it must be completely empty — the multipart exception does not apply at that level.

Test both paths, not just the happy one: delete a bucket with only aborted parts (should now succeed), delete one with a single real object (must still fail until emptied), and run your project-teardown flow end to end (must still empty everything first). The operators who get surprised here will be the ones who read "bucket deletion relaxed" and deleted their emptying logic wholesale.

This is a pattern, not a one-off​

The reason to treat November 2 as a runbook item rather than a ticket is that Hetzner removes things on schedule, and the schedule keeps producing dates. Two weeks ago, on October 1, the GET /v1/datacenters endpoints started returning HTTP 410 Gone exactly as announced back in June — and operators whose provisioning still resolved server-type availability through datacenter-scoped fields woke up to failing server creates with "API functionality was removed" until they switched to server_types[].locations[]. That is the preview of what happens to deprecated readers on November 2, except quieter, because a missing field returns nothing while a removed endpoint at least returns 410.

The changelog has more dates already queued. On September 23, Primary IPs started reporting assignee_type: "unassigned" instead of "server" with a null id — a semantic flip that breaks any automation branching on the old pair. And on October 12, Hetzner deprecated the HTTP Link headers on paginated responses with removal set for April 1, 2027, directing everyone to meta.pagination in the body. Your November 2 migration is also the rehearsal for April 1.

Three standing defenses, all cheap:

  • Subscribe to the changelog RSS feed. Every removal above was announced months ahead. The teams that scramble are the ones that learn about removals from their error logs.
  • Pin and upgrade SDKs deliberately. hcloud-go, hcloud-python, the Terraform provider, and the CLI all track these deprecations in their own changelogs (the datacenter removal, for instance, is flagged across all of them). A bump-and-grep per removal beats a surprise.
  • Grep before each deadline. Removed fields and endpoints are all string-matchable in your own code. Put the removal date on a calendar with a one-week lead, and run the grep then — not the morning after.

Pre-November-2 checklist​

  1. No controller, operator, script, dashboard, or Terraform module reads top-level deprecated on Images — all read deprecation (null = not deprecated).
  2. Server-type checks are per location: locations[] → deprecation for the provisioning target, not a global flag.
  3. Load Balancer Type consumers read the deprecation object (live since June 5), not the old date string.
  4. SDKs bumped to versions carrying the new fields, and exp/deprecationutil-style helpers used where they fit.
  5. Bucket-teardown scripts tested against the new rule: aborted-parts-only succeeds, single-real-object still fails until emptied.
  6. Project-teardown flows still fully empty every bucket (the multipart exception does not apply).
  7. Changelog RSS subscribed; April 1, 2027 (Link header removal) already calendared with a one-week grep lead.

Nineteen days is plenty — if the grep happens this week. The landlord API does not negotiate after the date, and unlike a Kubernetes upgrade, there is no skew window to hide in: on November 2 the field is simply gone.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex