| name | hcloud-remote |
| description | Day-to-day Hetzner Cloud ops from the CLI — list/show servers (prints IPv4/IPv6 cleanly), reboot, create and list snapshots, ssh. Resolves servers by name so you never paste IDs. Deliberately narrow — no volumes/networks/load balancers. |
| when_to_use | Trigger on phrases like "what's the IP of the hetzner box", "ssh into the vps", "reboot the vps", "take a snapshot before I...", "list hetzner snapshots", "hetzner status", anything that used to require curling api.hetzner.cloud + parsing JSON with a Python heredoc. |
| allowed-tools | Bash(hcloud-remote:*) Bash(python3 *hcloud-remote*:*) |
hcloud-remote
Stdlib-only Python 3 CLI for the narrow slice of Hetzner Cloud API you actually use when you have one or two VPSes and no Terraform. Lives at ${CLAUDE_SKILL_DIR}/../../bin/hcloud-remote; the plugin auto-adds bin/ to PATH.
When to reach for this
- User needs an IP address or status for a Hetzner server.
- User wants to reboot or snapshot before risky work.
- User wants to SSH in but doesn't have the IP in their head.
Do NOT use for: volumes, networks, firewalls, load balancers, floating IPs, image management, server creation/destruction. If you find yourself wanting any of those, reach for Terraform / hcloud (the upstream CLI) instead — this wrapper intentionally doesn't cover them.
When NOT to use this — fall back to hcloud CLI directly
This wrapper is deliberately tiny. It covers server list/show/reboot, snapshot create/list, and ssh — nothing else. For anything outside that slice, skip hcloud-remote and use the upstream hcloud CLI (or Terraform) directly. It's already authed on the same HCLOUD_TOKEN.
Specific cases where you should use hcloud ... (or Terraform) directly, not hcloud-remote:
- Creating or destroying servers. →
hcloud server create ... / hcloud server delete <name>. The wrapper has no server create or server delete on purpose — those are Terraform's job for anything you care about keeping.
- Volumes, networks, load balancers, floating IPs, firewalls. →
hcloud volume|network|load-balancer|floating-ip|firewall .... None of these are wrapped; don't try to coerce server show into revealing attached-volume details.
- SSH key management (adding/listing/rotating project SSH keys). →
hcloud ssh-key .... The wrapper's ssh subcommand just uses your local ~/.ssh/* — it doesn't manage Hetzner's registered keys.
- Image management beyond snapshots — deleting snapshots, listing backups/ISOs, rebuilding from an image. →
hcloud image .... The wrapper creates and lists snapshots but never deletes them (safety); prune via hcloud image delete <id> or the Console.
- Rescue mode, power on/off, rename, change type, resize. →
hcloud server enable-rescue|poweron|poweroff|rename|change-type .... Only reboot is wrapped.
Don't get stuck in a loop. If hcloud-remote --help doesn't show a subcommand for what the user wants, immediately switch to hcloud directly rather than guessing flags or chaining server show --json | jq to reverse-engineer missing functionality. The wrapper's job is to make the common narrow slice fast on Windows; everything else is hcloud territory.
Configure
Layered config, highest precedence first:
--env-file <path>
.env.local / .env (walked up from cwd)
- Shell environment (including Claude Code settings)
Project .env files override the shell. Only HCLOUD_* and HETZNER_* keys are picked up.
HCLOUD_TOKEN=...
Or globally in ~/.claude/settings.json:
{ "env": { "HCLOUD_TOKEN": "..." } }
Generate a token in Hetzner Console → Security → API tokens. Read/write scope is fine; snapshot + reboot both need write.
Commands
hcloud-remote server list
hcloud-remote server show <name>
hcloud-remote server reboot <name> [-y]
hcloud-remote snapshot create <name> --description "before schema migration"
hcloud-remote snapshot list [--server <name>]
hcloud-remote ssh <name>
hcloud-remote ssh <name> --user admin -- -p 2222 'uptime'
All list/show commands support --json. Every JSON payload carries a top-level tool: {name, version} field so you can verify which plugin version produced the output.
Post-processing with jq
For filtering, reshaping, or extracting fields from --json output, pipe into jq. It ships on most package managers (brew install jq, apt install jq, choco install jq) and beats ad-hoc grep/python -c one-liners for working with structured responses. Example: hcloud-remote server list --json | jq '.[] | {name, status, ipv4: .public_net.ipv4.ip}'.
Servers are resolved by name
Every command accepts either a name or a numeric id. You do not need to copy Hetzner IDs around.
hcloud-remote server show my-vps-01
hcloud-remote server show 12345678
The Python-heredoc bug this replaces
On Windows, piping curl output into python3 -c "..." with a multiline string gets mangled by the bash shim (stray || parsed as goto :error). The previous workaround was writing a temp .py file every time. This wrapper does the JSON parsing in-process, so:
hcloud-remote server show my-vps-01
just prints the IPs. No heredocs, no temp files.
Snapshots
Snapshots in Hetzner are a type of image. snapshot create queues the snapshot (returns immediately with an action id); the snapshot itself finishes in the background over a few minutes depending on disk size.
hcloud-remote snapshot create my-vps-01 --description "pre-coolify-upgrade"
hcloud-remote snapshot list --server my-vps-01
--server filters client-side to snapshots whose created_from.id matches that server; Hetzner's bound_to filter only matches currently-attached snapshots, which is rarely what you want.
ssh
hcloud-remote ssh my-vps-01
Resolves the server's public IPv4 and execs ssh root@<ip>. Pass extra ssh args after --:
hcloud-remote ssh my-vps-01 --user admin -- -p 2222 -i ~/.ssh/hetzner 'uptime'
On Unix, execs into ssh so Ctrl-C behaves. On Windows, runs ssh as a subprocess and propagates its exit code — near-identical UX.
Troubleshooting
HTTP 401: HCLOUD_TOKEN is wrong or expired. Tokens are shown once at creation — generate a new one.
HTTP 423 on reboot / snapshot: the server is locked (another action in flight). Wait and retry.
ssh: connect to host … port 22: Connection refused: server is mid-reboot, or SSH isn't on 22. Pass -- -p <port> after the -- separator.
- Snapshot quota exceeded: Hetzner limits snapshots per project. Prune via the Console or
hcloud CLI — this wrapper doesn't delete images (safety).