Home / Docs / Hetzner Cloud

Hetzner Cloud

Hetzner Cloud is a first-class provider in Servonaut — full lifecycle (list / create / power on / shutdown / power off / reboot / delete), project SSH-key registry, and 11 MCP tools your agent can call. Designed for disposable demo / staging fleets where hourly billing and clean teardown beat long-lived AWS-style fleets.

What you get

A Hetzner sidebar section with a Manager screen and SSH-key registry, three CLI verbs (servonaut hetzner list / create / destroy), and 11 hosted MCP tools — all backed by entitlements, audit, and the same confirmation protocol used elsewhere.

Setup

1. Get a Hetzner Cloud API token

Open the Hetzner Cloud Console, pick the project you want Servonaut to manage, and create a new API token under Security → API Tokens with Read & Write scope. Copy it once — Hetzner only shows it on creation.

2. Make the token available

Servonaut resolves the token from the first matching source:

#SourceNotes
1config.hetzner.api_tokenSupports $ENV_VAR and file:/path/to/token prefixes.
2$HCLOUD_TOKEN env varSame env var the official hcloud CLI / Terraform provider use.
3~/.config/hcloud/tokenDefault location of the hcloud CLI's token file — zero-config if you already have it.

Recommended: drop the token at ~/.config/hcloud/token so the same file works for the hcloud CLI, Servonaut, and Terraform.

bash
$ mkdir -p ~/.config/hcloud $ echo "abc...your-token..." > ~/.config/hcloud/token $ chmod 600 ~/.config/hcloud/token

3. Enable the provider

Either run the in-TUI Settings → Hetzner Setup wizard, or edit ~/.servonaut/config.json:

~/.servonaut/config.json
{ "hetzner": { "enabled": true, "api_token": "", "default_username": "root", "default_image": "ubuntu-22.04", "default_server_type": "cx23", "default_location": "fsn1", "default_hetzner_ssh_key": "my-laptop", "default_local_ssh_key": "~/.ssh/id_ed25519", "require_ssh_keys_on_create": true } }

The CLI commands work even with enabled: false, as long as the token chain resolves. The TUI and the chat panel honour the flag.

4. Validate

bash
$ servonaut hetzner test-connection → Connected. 0 server(s) in project.

TUI integration

With hetzner.enabled = true, the unified instance list shows Hetzner servers tagged provider: hetzner. SSH connect, run-command overlay, file browser, log viewer — all of these work transparently against Hetzner servers using the configured default_local_ssh_key and default_username (root by default — Hetzner's stock images don't ship a non-root user).

Hetzner Manager screen

The sidebar's Hetzner → ⚙ Manage entry opens a per-provider table with a state-aware lifecycle toolbar:

  • Create opens the create wizard (region → server type → image → SSH keys).
  • Power on / Shutdown / Power off / Reboot — buttons enable based on the selected row's state.
  • Delete — typed-name confirmation; permanent.

The create wizard pulls live pricing from the Hetzner catalog and pre-selects your default SSH key. If you have no SSH key registered, the wizard nudges you to add one before continuing — Servonaut refuses by default to create a server with no keys.

SSH key registry

Hetzner → 🔑 SSH Keys shows every Hetzner Cloud SSH key registered with the project. Add new ones from a public-key file, delete obsolete ones, copy the fingerprint. The same registry feeds the create-wizard's SSH-key picker, so what you see here is what gets injected into new servers.

CLI reference

bash
$ servonaut hetzner list [--json] [--state STATE] $ servonaut hetzner create NAME [--type cx23] [--image ubuntu-22.04] [--location fsn1] [--ssh-key NAME|ID] (repeatable) [--no-wait] [--json] $ servonaut hetzner destroy NAME_OR_ID [--yes] [--json] $ servonaut hetzner ssh-keys list [--json] $ servonaut hetzner ssh-keys add NAME --public-key-file PATH $ servonaut hetzner server-types [--json] $ servonaut hetzner test-connection [--json]

Examples

bash
# Spin up an Ubuntu 22.04 box in Falkenstein, inject the "laptop" SSH key $ servonaut hetzner create demo-1 --ssh-key laptop # List, filtering on state $ servonaut hetzner list --state running # Destroy without typed confirmation (CI / scripts) $ servonaut hetzner destroy demo-1 --yes # Browse types + prices $ servonaut hetzner server-types
Exit codeMeaning
0Success
1Generic error (API failure, network)
2Not configured (no token resolvable)
3Typed-confirmation declined for destroy
4Input validation error

MCP tools (11)

Eleven tools, registered automatically when the Hetzner service is wired up. They appear in tools/list for any agent (Claude Code, Cursor, Windsurf, …) that connects to the Servonaut MCP server. Mutating tools require mcp.guard_level = dangerous and gate behind the hetzner_mcp_operations entitlement (free=0, Solo=50, Teams=200/seat).

ToolGuardDescription
hetzner_list_serversreadonlyList servers in project
hetzner_list_server_typesreadonlyCatalog + EUR prices
hetzner_list_ssh_keysreadonlyRegistered SSH keys
hetzner_create_ssh_keystandardRegister a new SSH public key
hetzner_delete_ssh_keydangerousRemove an SSH key from the project
hetzner_create_serverdangerousCreate a server (auto-registers in fleet). Confirmation required.
hetzner_delete_serverdangerousPermanently delete a server. Confirmation required.
hetzner_power_ondangerousPower a stopped server back on
hetzner_power_offdangerousHard power-off (no graceful shutdown)
hetzner_shutdowndangerousGraceful ACPI shutdown
hetzner_rebootdangerousSoft reboot

Auto-registration into the fleet

hetzner_create_server automatically busts the local Hetzner cache on success, so the next call to list_instances (or any TUI refresh) sees the new server. Downstream tools — run_command, get_logs, transfer_file, build_server_memory — work on the new server within seconds, with no manual "add server" flow.

Security & safety notes

  • The API token is stored in ~/.servonaut/config.json, encrypted at rest if you opt in to a passphrase via the existing config-encryption flow (AES-256-GCM with PBKDF2-HMAC-SHA256, 600 000 iterations).
  • The token is stripped from any config-sync upload to servonaut.dev, so even if you opt in to cloud config sync, the token never leaves this machine.
  • The local cache and audit log are written with 0o600 permissions and O_NOFOLLOW to defeat symlink-redirect attacks. The cache uses an atomic temp+rename so a concurrent reader never sees a partial JSON.
  • hetzner_create_ssh_key does not log the public-key text — only the key name. API errors that may include public-key fragments are truncated to 160 chars in the audit reason.
  • hetzner_create_server refuses by default to create a server with no SSH keys (require_ssh_keys_on_create=true). Hetzner would otherwise spawn one with a random root password the CLI discards, leaving a billed unreachable box.

Troubleshooting

"No Hetzner Cloud API token configured"

The token chain didn't resolve. Place the token at one of:

  • config.hetzner.api_token (with optional $ENV_VAR / file: prefix)
  • $HCLOUD_TOKEN environment variable
  • ~/.config/hcloud/token (the standard hcloud CLI location)

"unsupported location for server type"

Hetzner deprecates server types per location every ~18 months. If your default_server_type is no longer available in default_location, the create call will fail. Run servonaut hetzner server-types to see what's currently available, then either pass --type per call or update the default in ~/.servonaut/config.json.

SSH host-key prompt on first connect

Hetzner does not return the new server's host fingerprint in the create response. Servonaut's SSH wrapper uses StrictHostKeyChecking=accept-new, so the first connect adds the host to your known_hosts automatically. No special handling required.

shutdown sent but server stays running

servonaut hetzner shutdown (and the hetzner_shutdown MCP tool) sends an ACPI signal to the guest. On a freshly-booted Hetzner cloud-init image, acpid isn't started yet during the first ~3 minutes, so the signal is dropped and the server stays in running state. The CLI/MCP call returns success because the API accepted the signal — guest cooperation is what didn't happen.

Two options:

  • Wait until the server has fully booted (cloud-init finished, systemctl is-system-running returns running) before issuing shutdown.
  • Use servonaut hetzner power off / hetzner_power_off instead — that's a hard power-off via the Hetzner API which doesn't depend on the guest. Risks in-flight write loss, so prefer shutdown on settled servers and power off on fresh ones or when the guest is unresponsive.

Anti-scope (deliberately not supported)

The following are out of scope for the current release:

  • Hetzner Robot (dedicated servers) — different API surface.
  • Storage Boxes, DNS, Load Balancers, Volumes, Networks, Firewalls, Floating IPs, Placement Groups — manage these in the Hetzner Console.
  • Snapshot / image management.
Documentation