Out-of-stock radar for Hetzner Cloud β watch Β· match Β· email
Polls the Hetzner Cloud API for a given server type in a given location and emails you the moment
it comes back in stock. A single-file Node script β point it at a CX/CCX/CAX type and a
datacenter, run it, and walk away.
- Explore β What it is Β· Quick start Β· Configuration Β· How it works Β· Tech stack Β· Docs
hetzner-server-radar is a small watcher for Hetzner Cloud capacity. Popular server types
(especially the dedicated CCX line and the cheap CX/CAX types) routinely sell out in specific
datacenters; this tool polls the public datacenter API on a fixed cadence and sends an email alert
the moment your chosen type is available where you want it.
Defaults: CX33 (id 115) in Nuremberg (nbg1), checking every 5 minutes. Everything is
configurable via .env. While a type stays available the tool stays quiet β it alerts on the
transition into stock, then waits out a cooldown before it can fire again, so you get a heads-up
rather than a flood.
Requires Node 22 (see .nvmrc).
nvm use # picks up .nvmrc
npm install
cp .env.example .env
# edit .env: fill in HCLOUD_TOKEN, RESEND_API_KEY, ALERT_EMAIL_TO
npm start| Script | What it does |
|---|---|
npm start |
run the radar (tsx src/index.ts) |
npm run dev |
run with file-watch reload |
npm run typecheck |
tsc --noEmit |
Stop with Ctrl-C.
All settings come from .env β copy .env.example and fill in the three required
secrets.
| Var | Required | Default | Notes |
|---|---|---|---|
HCLOUD_TOKEN |
yes | β | Hetzner Cloud API token; read scope is enough |
RESEND_API_KEY |
yes | β | from https://resend.com |
ALERT_EMAIL_TO |
yes | β | alert recipient |
SERVER_TYPE_ID |
no | 115 (CX33) |
Hetzner server type id |
LOCATION_PREFIX |
no | nbg1 (Nuremberg) |
matched against dc.name.startsWith(...) |
CHECK_INTERVAL_SECONDS |
no | 300 |
poll cadence in seconds |
EMAIL_COOLDOWN_SECONDS |
no | 3600 |
min seconds before a new alert can fire; suppresses flapping |
HETZNER_PROJECT_ID |
no | β | when set, the "Buy now" link deep-links to that project's create-server page (find it in the console URL: https://console.hetzner.cloud/projects/<id>/...) |
Matched with dc.name.startsWith(...), so use a location name (or a specific datacenter name for
finer scope). Verified against GET /v1/datacenters on 2026-05-10.
| Prefix | City | Country | Datacenter |
|---|---|---|---|
nbg1 |
Nuremberg | DE | nbg1-dc3 |
fsn1 |
Falkenstein | DE | fsn1-dc14 |
hel1 |
Helsinki | FI | hel1-dc2 |
ash |
Ashburn, VA | US | ash-dc1 |
hil |
Hillsboro, OR | US | hil-dc1 |
sin |
Singapore | SG | sin-dc1 |
All active server types from GET /v1/server_types (verified 2026-05-10). Specs are cores / memory
GB / disk GB.
| ID | Name | Specs | CPU | Arch |
|---|---|---|---|---|
22 |
cpx11 | 2 / 2 / 40 | shared | x86 |
23 |
cpx21 | 3 / 4 / 80 | shared | x86 |
24 |
cpx31 | 4 / 8 / 160 | shared | x86 |
25 |
cpx41 | 8 / 16 / 240 | shared | x86 |
26 |
cpx51 | 16 / 32 / 360 | shared | x86 |
45 |
cax11 | 2 / 4 / 40 | shared | arm |
93 |
cax21 | 4 / 8 / 80 | shared | arm |
94 |
cax31 | 8 / 16 / 160 | shared | arm |
95 |
cax41 | 16 / 32 / 320 | shared | arm |
96 |
ccx13 | 2 / 8 / 80 | dedicated | x86 |
97 |
ccx23 | 4 / 16 / 160 | dedicated | x86 |
98 |
ccx33 | 8 / 32 / 240 | dedicated | x86 |
99 |
ccx43 | 16 / 64 / 360 | dedicated | x86 |
100 |
ccx53 | 32 / 128 / 600 | dedicated | x86 |
101 |
ccx63 | 48 / 192 / 960 | dedicated | x86 |
108 |
cpx12 | 1 / 2 / 40 | shared | x86 |
109 |
cpx22 | 2 / 4 / 80 | shared | x86 |
110 |
cpx32 | 4 / 8 / 160 | shared | x86 |
111 |
cpx42 | 8 / 16 / 320 | shared | x86 |
112 |
cpx52 | 12 / 24 / 480 | shared | x86 |
113 |
cpx62 | 16 / 32 / 640 | shared | x86 |
114 |
cx23 | 2 / 4 / 40 | shared | x86 |
115 |
cx33 | 4 / 8 / 80 | shared | x86 |
116 |
cx43 | 8 / 16 / 160 | shared | x86 |
117 |
cx53 | 16 / 32 / 320 | shared | x86 |
93β95, 116, 117 are listed by the API but not in any datacenter's available list at the
time of verification β pick one of these only if you're specifically waiting for it to come back in
stock.
src/index.ts calls GET https://api.hetzner.cloud/v1/datacenters every
CHECK_INTERVAL_SECONDS, keeps datacenters whose name starts with LOCATION_PREFIX, and checks
whether their server_types.available list contains SERVER_TYPE_ID.
flowchart LR
Timer["β±οΈ poll loop"] --> DC["Hetzner /v1/datacenters"]
DC --> Filter["filter by LOCATION_PREFIX\n+ SERVER_TYPE_ID available"]
Filter -- "match (newly in stock)" --> Resend["Resend email alert"]
Filter -- "no match / cooldown" --> Timer
Resend --> Inbox["π¬ your inbox"]
On a transition into "available" it sends an alert email through Resend (subject + plaintext/HTML
body, with an optional "Buy now" deep-link). It does not re-alert every cycle while the type
stays in stock; once it drops out and comes back, a new alert can fire after EMAIL_COOLDOWN_SECONDS
has elapsed.
| Layer | Tech |
|---|---|
| Runtime | Node 22 (ESM), run via tsx |
| Language | TypeScript 6 (strict, noEmit typecheck) |
| Data source | Hetzner Cloud public API (/v1/datacenters, /v1/server_types, /v1/locations) |
| Resend | |
| Config | dotenv (.env) |
This is a single-file utility β the full behavior lives in src/index.ts and the
configuration reference above. There is no separate docs/ tree.
Not yet present in this repo: a
LICENSEfile, a CI workflow (.github/workflows/), and adocs/directory. Add these if the project grows beyond a single script.