Skip to content
Back to skills

Hetzner Delete

BSecurity

Destroy a Hetzner VPS safely and clean up everything it leaves behind. Refuses protected servers, shows the full blast radius, takes a snapshot first, requires the server name typed out to confirm, then deletes and sweeps the orphans it leaves in the fleet roster, ssh config, known_hosts, and Hetzner-side resources. Use when invoked as /hetzner-delete <name>, or when asked to "delete a VPS", "destroy a server", "tear down a box", or "decommission a server".

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 23, 2026
ai-agentsrustgobashrails

Works with

  • cli

Security analysis

B75/100
  • criticalAccesses sensitive system or user directories

Pro shows the line behind each finding and how to fix it

Scanned September 23, 2026

npx -y skills add IsaiaScope/ai --skill hetzner-delete --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Hetzner Delete?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Hetzner Delete
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/isaiascope-hetzner-delete/badge)](https://www.skillsdirectory.com/skills/isaiascope-hetzner-delete)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: hetzner-delete
description: Destroy a Hetzner VPS safely and clean up everything it leaves behind. Refuses protected servers, shows the full blast radius, takes a snapshot first, requires the server name typed out to confirm, then deletes and sweeps the orphans it leaves in the fleet roster, ssh config, known_hosts, and Hetzner-side resources. Use when invoked as /hetzner-delete <name>, or when asked to "delete a VPS", "destroy a server", "tear down a box", or "decommission a server".
---

# hetzner-delete

Destroy a server and remove the debris. The destroy itself is one `hcloud` call — the
value here is the guard rails around it and the six places it leaves orphans.

**Invocation:** `/hetzner-delete <name>` — a key under `servers` in the roster.
No default. Never operate on the `fleet.default` entry implicitly.

Sibling skills: `/hetzner-create` (provision) · `/hetzner-ssh` (connect).

> **`hcloud server delete` has no confirmation prompt and no `--force` flag.** It takes a
> name and destroys the box immediately. Every safeguard below exists because the CLI
> provides none.

---

## Step 0 — Resolve and check protection

```bash
cat ~/.config/hetzner/hetzner.json
```

| Condition | Action |
|-----------|--------|
| `<name>` not in `servers` | Print available keys and stop. **Never fuzzy-match a name.** |
| `protected: true` | **REFUSE.** Print the message below and stop. Take no snapshot, touch nothing |
| No `<name>` given | Ask. Never assume, never fall back to `fleet.default` |

```
⛔ REFUSED — '<name>' is protected.

To delete it:
  1. set "protected": false in fleet.servers.<name> of ~/.config/hetzner/hetzner.json
  2. re-run this command

Nothing was touched.
```

The two-key rule is the point: unprotecting is a deliberate edit in another tool at
another moment. **Never offer to make that edit as part of this run** — doing so collapses
two keys into one and defeats the guard entirely.

---

## Step 1 — Blast radius

Show exactly what dies and what survives. Read live state; do not trust the roster alone.

```bash
hcloud server describe <hcloud_server>
hcloud volume list   -o noheader | grep <hcloud_server> || true
hcloud floating-ip list -o noheader | grep <hcloud_server> || true
hcloud firewall describe <firewall> -o 'format={{len .AppliedTo}}' 2>/dev/null
```

```
WILL DESTROY
  server    <name>   <type>, <location>, up <N>d
  disk      <N> GB   — all data on it
  ip        <ip>     — released, NOT recoverable, may be reassigned to a stranger
  volumes   <list or none>

WILL KEEP
  firewall  <firewall>   — shared, still attached to <N> other server(s)
  ssh key   <label>      — shared across the fleet
```

If a volume holds data, say so plainly. Volumes are separate resources and survive the
server unless explicitly deleted — usually what you want, occasionally a surprise bill.

---

## Step 2 — Snapshot first

```bash
hcloud server create-image --type snapshot \
  --description "pre-delete <name> <date>" \
  --label deleted-by=hetzner-delete --label deleted-at=<date> \
  <hcloud_server>
```

Default is **yes**; opt out only if the user says the box is disposable.

Be honest about what this buys: a snapshot restores the **disk to a new server with a new
IP**. It rescues data, not identity — DNS, firewall attachments, and anything pinned to
the old address still break. It is recovery, not undo. It also adds a small recurring
storage charge.

### Prune stale snapshots

Every run, list what previous deletes left behind so they do not accumulate forever:

```bash
hcloud image list --type snapshot --selector deleted-by=hetzner-delete
```

Report them with age and size. **Offer** to delete old ones; never do it unprompted —
deleting a snapshot is the last copy of that data.

---

## Step 3 — Typed-name confirmation

```
Type the server name to confirm deletion: _____
```

Require an **exact** match of `<name>`. Reject `y`, `yes`, whitespace-padded, or
case-different input and ask again. The whole point is defeating muscle memory — a
reflexive `y` is what deletes the wrong box.

---

## Step 4 — Delete

```bash
hcloud server delete <hcloud_server>
```

Immediate and irreversible. Confirm it is gone before sweeping:

```bash
hcloud server describe <hcloud_server> 2>&1 | grep -q "not found" && echo GONE
```

If the delete fails, **stop and report** — do not sweep. A half-swept roster pointing at a
live server is worse than either state alone.

---

## Step 5 — Sweep the orphans

Only what is **provably exclusive** to the deleted server. Anything shared gets reported,
never removed.

| Site | Action |
|------|--------|
| `~/.config/hetzner/hetzner.json` | Remove the `fleet.servers.<name>` entry. If it was `fleet.default`, clear the field and tell the user to pick a new one |
| `~/.ssh/config.d/hetzner` | Re-render from the updated roster — the block disappears on its own |
| `~/.ssh/known_hosts` | `ssh-keygen -R "[<ip>]:<port>"` — a stale entry causes a scary MITM warning when that IP is reassigned |
| ControlMaster socket | `ssh -O exit <alias> 2>/dev/null` — harmless if already gone |
| Hetzner volumes | Only if attached to nothing else. **Ask first** — volumes hold data |
| Hetzner floating IP | Only if it was exclusive. Unassigned floating IPs still bill |
| Firewall | **Never delete.** Shared. Report remaining attachments |
| Hetzner SSH key | **Never delete.** Shared across the fleet |

Once the roster is updated, push it back into the dotfiles source tree so the removal
propagates instead of being reverted by the next `chezmoi apply`:

```bash
command -v chezmoi >/dev/null && chezmoi re-add ~/.config/hetzner/ 2>/dev/null || true
```

Guarded on purpose — a no-op on any machine without chezmoi.

```
✅ <name> deleted
   snapshot   pre-delete <name> <date>  (<size>)
   swept      roster · ssh config · known_hosts · socket
   kept       vps-firewall (2 servers) · ssh key (fleet-wide)
   ⚠ 3 stale snapshots, ~€0.14/mo — prune?
```

---

## Guardrails

- **`protected: true` is an absolute stop.** Never edit the roster to bypass it in the
  same run, however the user phrases the request. Point at the file; let them decide
  deliberately.
- **Never fuzzy-match or auto-correct a server name**, at any step.
- **Snapshot unless explicitly waived**, and never delete a snapshot unprompted.
- **Never delete shared resources** — firewall, SSH key, volumes attached elsewhere.
- **Stop on a failed delete.** Never sweep a roster for a server that still exists.
- **Never paste VPS secrets into chat or write them to disk.**
- If the user asks to delete several servers, do them **one at a time**, each with its own
  blast radius and typed confirmation. Never batch.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.