On October 1, 2026 — three weeks from today — every call your automation makes to Hetzner's datacenters API starts returning HTTP 410 Gone. Not a deprecation warning, not a sunset header you can snooze: gone. And that is only half the breakage. Since June, Hetzner has also been retiring server-type names your machine templates treat as stable identifiers, while its own API tells you the old availability fields can no longer be trusted. If you run a Cluster API fleet on Hetzner, here is the full migration checklist, up front, so you can stop reading and start grepping.
| # | What breaks | Replace with | Where it lives in your fleet |
|---|---|---|---|
| 1 | GET /v1/datacenters and GET /v1/datacenters/{id} → 410 Gone after Oct 1 | GET /v1/locations plus the server_types[].locations matrix | Capacity scripts, cost tooling, anything polling datacenter enumeration |
| 2 | datacenter.server_types.{supported, available, available_for_migration} — stale since Apr 1 | server_types[].locations[].available / available_for_migration | Placement logic deciding which type fits which region |
| 3 | datacenter field on Servers and Primary IPs — already removed in July | Top-level location field | Server-create calls, Terraform configs, IP automation |
| 4 | Pinned server-type names (cx22, old ccx*) silently going stale | Re-validated names from the live server_types list | HCloudMachineTemplate spec.type, Terraform server_type, node-pool scripts |
The rest of this post substantiates each row: the dates, the exact replacements, and the ordered runbook for the three weeks you have left.
The timeline: five dates, one deadline
Hetzner did not drop this all at once. The phase-out has been rolling for almost a year, which is why so many fleets are halfway migrated without realizing the halfway state is the dangerous one:
- October 16, 2025 — older server types deprecated in the Cloud changelog. Names your templates may still pin began their countdown here.
- December 16, 2025 — the
datacenterproperty in Server and Primary IP requests and responses deprecated in favor of a top-levellocationproperty, with removal slated for after July 1, 2026. That removal has already happened. - April 1, 2026 — the
datacenter.server_types.{supported, available, available_for_migration}fields deprecated and explicitly marked "no longer guaranteed" accurate. Any placement decision read from these fields has been suspect for five months. - June 2, 2026 — the datacenters endpoints themselves deprecated:
GET /v1/datacentersandGET /v1/datacenters/{id}will returnHTTP 410 Goneafter October 1, 2026. - June 15, 2026 — the pricing update that retired the CX22/CX32/CX42 line in favor of CX23/CX33/CX43 for new orders and rescales, alongside steep increases on some plans.
October 1 is the only date still in the future. Everything else is already true, which means rows 2–4 of the checklist can bite today, while row 1 has a three-week fuse.
Breakage 1: your datacenter enumeration starts 410ing
This is the loudest breakage and the easiest to find. After October 1, both datacenter endpoints return 410 Gone — the status code APIs use when a resource is deliberately, permanently removed, not merely moved. Retry logic will not save you; there is nothing to retry toward.
The replacement is the locations API. Where you previously enumerated datacenters and read their supported server types, you now enumerate locations and read each server type's per-location availability matrix. Concretely, audit for these patterns:
# Direct API consumers — the critical grep
grep -rn "datacenters" --include="*.py" --include="*.go" --include="*.sh" --include="*.ts" .
grep -rni "hcloud_datacenter" --include="*.tf" . # Terraform: hcloud_datacenter(s) data sources are deprecated
grep -rn "hcloud datacenter" --include="*.sh" --include="*.md" . # CLI: datacenter list/describe are deprecatedThe official clients have already done their half: hcloud-go, hcloud-python, the hcloud CLI, the Terraform provider, and the Ansible collection all shipped deprecation releases pointing at hcloud_location / hcloud_locations data sources and location list commands. So the fix gradient is clear — if you consume Hetzner through an up-to-date SDK or provider, bump the dependency and rename the data source. If you call the REST API directly, which capacity-checking and cost-attribution scripts very often do, you own the rewrite: swap the enumeration root from /v1/datacenters to /v1/locations and re-derive availability from server_types[].locations.
One subtlety worth stating plainly: Hetzner's model is that a location (Nuremberg, Falkenstein, Helsinki, Singapore, Ashburn, Hillsboro) contains datacenters, and server-type availability is now expressed per location. If your tooling joined datacenter records to server types to answer "can I place a cpx node in fsn1?", that join now happens against the location's server-type matrix instead. Same question, different root object.
Breakage 2: server-type names are not stable identifiers
This is the quiet breakage, and for a CAPH fleet it is the more dangerous one. Hetzner recycles its server-type taxonomy every 12–18 months. The June 2026 round retired CX22/CX32/CX42 in favor of CX23/CX33/CX43 — but that is one instance of a standing pattern, not a one-off. The older shared-vCPU cx21/cx31/cx41/cx51 and dedicated-vCPU ccx12–ccx62 lines were already removed, the current taxonomy spans shared x86 (cx), performance AMD (cpx), dedicated x86 (ccx), and Ampere Arm (cax), and deprecated types disappear from the API List endpoint entirely. Even the deprecated boolean on the server-type response is itself now deprecated — Hetzner wants you tracking the changelog and the live list, not a flag.
Three consequences for fleet automation:
First, treat every pinned type name as suspect until re-validated. Your source of truth is the live list, not your git history:
hcloud server-type list -o json | jq -r '.[].name' | sort > live-types.txt
grep -rhoP '(cx|cpx|ccx|cax)\d+' --include="*.yaml" --include="*.tf" . | sort -u | while read -r t; do
grep -qx "$t" live-types.txt || echo "STALE: $t"
doneAny pinned name missing from the live list is already uncreatable — pick a successor in the same CPU/memory class before the next scale-up tries. Note what this check does not rely on: the deprecated boolean, which Hetzner has itself deprecated. Absence from the list is the signal; the flag is legacy.
Second, availability is per location, and it moves. A type that exists is not a type you can place everywhere: new generations land in a subset of locations first and backfill later. The check is the locations matrix on each server type (available, available_for_migration), which is exactly the field family that replaced the stale datacenter-level ones from row 2 of the checklist. Bake that lookup into provisioning rather than assuming a type valid in Nuremberg is valid in Singapore.
Third, CAPH will not catch a stale name for you. As of v1.0.7, Cluster API Provider Hetzner deliberately stopped validating HCloudMachine.spec.type, precisely because types churn faster than the provider can track — it is now the operator's job to supply a valid, available type. A stale name therefore fails at reconcile time, when the controller tries to create the server against the live API, not at kubectl apply time when you could have caught it cheaply. If your CI validates manifests, add a step that resolves every spec.type against the live server-type list; clusterctl-side dry runs cannot see this failure.
Breakage 3: datacenter is already dead in server and IP calls
While everyone watches the October endpoint deadline, the field-level removal already happened. The datacenter property on Server and Primary IP create/read payloads was removed after July 1, 2026. Anything still sending it is sending a field the API no longer honors; anything still parsing it out of responses is parsing air.
The fix is mechanical — location replaces datacenter as the top-level placement field — but the blast radius is wider than one struct. Check Terraform modules (server resources pinning a datacenter instead of a location), Ansible playbooks, Hetzner Cloud Controller Manager configs, and any provisioning wrapper that defaults a datacenter like fsn1-dc14. The Fedora CoreOS docs pattern of DATACENTER="fsn1-dc14" next to TYPE="cx22" is the canonical example of a snippet that is now wrong twice over: a removed field holding a retired type.
The three-week runbook, in order
Do these in sequence; each step's output feeds the next:
- Find the 410 surface (row 1). Run the three greps from the Breakage 1 section across every repo that touches the Hetzner API — fleet configs, cost scripts, sidecar tooling. Bump SDK/CLI/Terraform-provider versions first; rewrite direct REST calls second.
- Re-validate every pinned type (row 4). Diff each
spec.type,server_type, and scripted type string againsthcloud server-type list. Replace retired names with current-generation equivalents in the same class, and confirm each replacement's per-location availability for every location your MachineDeployments target (row 2's matrix). - Purge the
datacenterfield (row 3). Swap tolocationin API payloads, Terraform, and playbooks. This one is already live breakage, so prioritize any path that creates servers or primary IPs today. - Add the CI guard. A scheduled job that resolves all pinned types and placement fields against the live API turns the next Hetzner taxonomy churn — and there will be one, on roughly a yearly cadence — from an outage into a pull request.
There is a broader point hiding in this checklist. Cheaper owned hardware is real money saved, but it arrives with API-surface maintenance a hosted platform's customer never sees: no Heroku or Render user spent an afternoon renaming datacenter fields. Owning the machines means owning the churn underneath them, and the teams that do it well treat the provider changelog as a tracked input to fleet ops, not background reading.
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.



