Przejdź do treści
Biblioteka skilli
DevOps i chmura · Automatyzacja

hetzner

/hetzner

Pomaga postawić i zabezpieczyć VPS w Hetzner Cloud: dobór planu i lokalizacji, reprodukowalne uruchomienie przez hcloud i cloud-init, konfiguracja SSH i Cloud Firewall.

Repozytorium
rsc-harness ↗
Licencja
MIT
Ostatnia zmiana repozytorium
25.10.2026

Ocena i bezpieczeństwo

100/ 100Klasa: dobry
Format25 / 25
Frontmatter z nazwą i opisem, poprawna nazwa, opis do 1024 znaków, nazwa zgodna z folderem.
Odkrywalność25 / 25
Opis mówi, kiedy użyć skilla, ma sensowną długość i jest w 3. osobie.
Treść35 / 35
Co najmniej 150 słów i do 500 linii, nagłówki, listy kroków, przykłady albo kod.
Higiena15 / 15
Bez zaślepek typu TODO, bez ścieżek Windows, z licencją.
Ryzykobrak sygnałów w skanie
Pliki w paczce
6
Skrypty
1
Komentarze HTML
0
Adresy URL w SKILL.md
0
Przestarzałość

Brak sygnałów. Repozytorium zmienione 25.10.2026, bez odwołań do wycofanych modeli i API.

Licencja

MIT, otwarta: można używać i zmieniać, z zachowaniem informacji o autorze.

Co sprawdzić przed instalacją
  • 1 skrypt w paczce. Przejrzyj je przed instalacją, bo Claude może je uruchomić.

Skrypty: scripts/verify.sh

Co robi i kiedy

Pomaga postawić i zabezpieczyć VPS w Hetzner Cloud: dobór planu i lokalizacji, reprodukowalne uruchomienie przez hcloud i cloud-init, konfiguracja SSH i Cloud Firewall.

Oryginalny opis (EN)

Use when provisioning or hardening a Hetzner Cloud VPS: picking the plan/location (CX/CPX/CAX/CCX), bringing it up reproducibly with hcloud + cloud-init, locking down SSH, and wiring the Cloud Firewall to hand off a Docker-ready host. NOT app deploys on Coolify (that is `coolify`), NOT Dockerfiles (that is `docker`), NOT another provider (that is `digitalocean`).

Jak działa

  1. Decide before you create
  2. Provision reproducibly
  3. Two firewalls, in order
  4. SSH hardening rules
  5. Hand off to Docker / Coolify
  6. Day-2
  7. Anti-patterns
  8. Verify
Sekcje z nagłówków SKILL.md, w oryginale · 197 linii, 1 510 słów
Podgląd SKILL.md
---
name: hetzner
description: "Use when provisioning or hardening a Hetzner Cloud VPS: picking the plan/location (CX/CPX/CAX/CCX), bringing it up reproducibly with hcloud + cloud-init, locking down SSH, and wiring the Cloud Firewall to hand off a Docker-ready host. NOT app deploys on Coolify (that is `coolify`), NOT Dockerfiles (that is `docker`), NOT another provider (that is `digitalocean`)."
tags: [hetzner, vps, cloud-firewall, hcloud, ssh-hardening, cloud-init, self-hosting]
recommends: [coolify, docker, secure-coding, domains-dns, backups, monitoring]
origin: risco
---

# Hetzner — the cheap European box, made safe and reproducible

Hetzner Cloud is chosen for one reason: price/performance. A 2 vCPU / 4 GB AMD
box runs about €7.99/mo, the EU Intel line dips under €4, and EU locations
include 20 TB of egress. The risk is that "cheap" becomes "unhardened and
unmonitored" — a root-SSH box on a public IPv4 with password auth on. Your job
is to make the cheap box **safe** and **reproducible**: every server comes up
from a committed cloud-init file and a Cloud Firewall ruleset, never from
click-ops in the console. A box you can't recreate from a file isn't a box, it's
a pet.

## Decide before you create

Pick the line, then the location. Prices are post-2026-04-01 (a price adjustment
took effect that date); treat exact cents as "verify in the console."

| Line | Chip | When to pick | Price band | EU-only? |
|---|---|---|---|---|
| **CX** | Intel (shared) | Cost-optimized, tiny EU workloads | ~€3.99/mo (2 vCPU/4 GB/40 GB) | Yes |
| **CPX** | AMD (shared) | Default for apps; best general value | CPX22 ~€7.99, CPX32 ~€13.99, CPX42 ~€25.49 | No |
| **CAX** | ARM64 (shared) | Cheapest per-core; ARM-clean workloads | Cheapest per-core | Yes |
| **CCX** | Dedicated vCPU | Steady CPU load, no noisy-neighbor | CCX13 ~€15.99 (2 vCPU/8 GB) | No |

Rules:

- **IPv4 costs extra (~€0.50–0.60/mo); IPv6 is free.** An IPv6-only box is
  cheaper, but it breaks anything that can't reach IPv6: many container
  registries, some package mirrors, CI runners, SSH clients on IPv4-only nets.
  If `docker pull` or `apt` will run on the box, keep one IPv4 unless you've
  confirmed every upstream is dual-stack. Why: a saved €0.50/mo is not worth a
  broken `docker pull` at 2 a.m.
- **Location decides your traffic budget.** EU locations (Nuremberg, Falkenstein,
  Helsinki) include **20 TB/mo** egress; overage ~€1/TB. US (Ashburn, Hillsboro)
  and Singapore have **far lower** included transfer. Why: pick US "for latency,"
  serve a video, and the surprise bill is the traffic cap, not the instance.
- **No APAC own datacenter.** If your users are in Asia, this is a real latency
  cost — name it, don't hide it.

Full dated matrix, latency notes, and the no-SLA / no-managed-DB reality:
`references/plans-and-locations.md`.

## Provision reproducibly

Install the official CLI (latest **v1.65.0**, released 2026-05-21) and create a
context (the token comes from the project's *Security → API tokens*, Read &
Write):

```bash
brew install hcloud            # or: see github.com/hetznercloud/cli releases
hcloud context create my-project   # paste the Read+Write API token when prompted
hcloud server-type list        # confirm names/prices before you create
```

Bring the box up with cloud-init so hardening happens **before first login**:

```bash
hcloud server create \
  --name app-01 \
  --type cpx22 \
  --location fsn1 \
  --image debian-12 \
  --ssh-key my-laptop \
  --firewall app-edge \
  --user-data-from-file cloud-init.yaml
```

Use `--location`, not `--datacenter`: the `datacenter` attribute is **deprecated
and removed after 2026-07-01** for Servers and Primary IPs. Trimmed cloud-init
skeleton (full annotated file in `references/cloud-init.md`):

```yaml
#cloud-config
users:
  - name: deploy
    groups: [sudo]
    shell: /bin/bash
    sudo: ["ALL=(ALL) NOPASSWD:ALL"]
    ssh_authorized_keys:
      - ssh-ed25519 AAAA... you@laptop   # your real public key, not a placeholder
disable_root: true                       # no root login at all
ssh_pwauth: false                        # no password auth, anywhere
package_update: true
packages: [ufw, fail2ban, unattended-upgrades]
runcmd:
  - sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
  - sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
  - ufw default deny incoming && ufw allow 22 && ufw allow 80 && ufw allow 443 && ufw --force enable
  - systemctl enable --now fail2ban
  - systemctl restart ssh
```

Rule: **never create a box with a bare root password and "I'll harden it later."**
Later is a window where the box is reachable as root with password auth on, and
Hetzner IPv4 space is scanned constantly. Bake it into boot.

## Two firewalls, in order

The Cloud Firewall runs at the network edge — stateful, applied before the
packet reaches the VM, and survives a misconfigured host. Create and apply it
**with or before** the server:

```bash
hcloud firewall create --name app-edge
hcloud firewall add-rule app-edge --direction in --protocol tcp --port 22  --source-ips 0.0.0.0/0 --source-ips ::/0
hcloud firewall add-rule app-edge --direction in --protocol tcp --port 80  --source-ips 0.0.0.0/0 --source-ips ::/0
hcloud firewall add-rule app-edge --direction in --protocol tcp --port 443 --source-ips 0.0.0.0/0 --source-ips ::/0
hcloud firewall apply-to-resource app-edge --type server --server app-01
```

Inbound is default-deny — you only describe what you *allow*. Then the host
`ufw` (set up in cloud-init above) is defense-in-depth: if you ever detach or
fat-finger the Cloud Firewall, the box still isn't wide open. Why both: the edge
firewall is your primary defense, but a single layer is a single point of
failure, and the two are configured through different surfaces (API vs host).

Tighten SSH to your own IP/range once you know it:

```bash
hcloud firewall delete-rule app-edge --direction in --protocol tcp --port 22 --source-ips 0.0.0.0/0 --source-ips ::/0
hcloud firewall add-rule    app-edge --direction in --protocol tcp --port 22 --source-ips 203.0.113.4/32
```

## SSH hardening rules

- **Key-only auth.** `PasswordAuthentication no`, `PubkeyAuthentication yes`. A
  password is brute-forceable; an ed25519 key is not.
- **No root login.** `PermitRootLogin no` + a sudo user. Root over SSH is the
  single most-targeted login on the internet.
- **fail2ban on.** Bans IPs after repeated failures — cheap insurance for the
  one port you must expose.
- **Moving port 22 is obscurity, not security.** It quiets log noise; it does not
  harden anything. Do it if you like clean logs, but never *instead* of key-only
  + a firewall.

## Hand off to Docker / Coolify

"Host ready" means all of the following are true:

- [ ] `ssh root@<ip>` is refused; `ssh deploy@<ip>` works with the key only.
- [ ] `sshd -T | grep -E 'permitrootlogin|passwordauthentication'` shows both `no`.
- [ ] Cloud Firewall attached; only 22 (scoped)/80/443 inbound.
- [ ] `ufw status` enabled with the same allowlist.
- [ ] `unattended-upgrades` and `fail2ban` running.

This skill stops at a clean, hardened host. Then route the install/deploy:
[`coolify`](../coolify/SKILL.md) for the Coolify install + app deploy flow,
[`docker`](../docker/SKILL.md) for Dockerfiles, compose, and image hardening.

## Day-2

- **Snapshot ≠ backup.** A snapshot is a one-off manual image you can boot from;
  the **Backups** add-on is automated, rotating, ~20% of the server price. A
  snapshot you took once in March is not a backup strategy — for that, see
  [`backups`](../backups/SKILL.md).
- **Resize grows, never shrinks.** You can scale the disk up; you cannot scale it
  back down. Size conservatively or you're stuck paying for it.
- **Volumes** for data you want to outlive/detach from the server. Keep databases
  and uploads on a volume so a server rebuild doesn't take the data with it.
- **Reverse DNS** must match for outbound mail to be accepted — set the PTR in the
  console/`hcloud` if the box sends email. Forward DNS records belong to
  [`domains-dns`](../domains-dns/SKILL.md).
- **No SLA, no managed DB.** There's no uptime guarantee, no managed-database
  product, and phone support only for dedicated-server customers — you run
  Postgres yourself, you monitor it yourself. For monitoring and alerting, see
  [`monitoring`](../monitoring/SKILL.md); treat the box as something you must
  watch, not something Hetzner watches for you. Name the trade-off to the user;
  don't pretend it's AWS.

## Anti-patterns

| Anti-pattern | Why it's wrong | Do instead |
|---|---|---|
| Root SSH + password auth left on | The most-scanned login on the public internet; bots find it in minutes | `PermitRootLogin no`, `PasswordAuthentication no`, key + sudo user |
| Only host `ufw`, no Cloud Firewall | A host misconfig or reset exposes everything; no edge layer | Cloud Firewall default-deny first, `ufw` as defense-in-depth |
| `--source-ips 0.0.0.0/0` on everything "temporarily" | Temporary rules become permanent; the whole box is exposed | Scope inbound to 80/443 public, SSH to your IP/range |
| Click-ops in the console | Not reproducible — you can't recreate or review the box | cloud-init file + `hcloud` commands committed to the repo |
| Snapshot treated as backup | One stale manual image, no rotation, no schedule | Enable the Backups add-on or push to off-box storage |
| US location, then surprised by the bill | US/Singapore include far less than EU's 20 TB egress | EU location for egress-heavy apps; check the traffic cap first |
| IPv6-only to save €0.50 | Registry/CI/mirror pulls over IPv4-only break | Keep one IPv4 unless every upstream is confirmed dual-stack |
| Using `--datacenter` | Deprecated, removed after 2026-07-01 | Use `--location` |

## Verify

```bash
ssh -o BatchMode=yes root@<ip>                 # expect: refused / permission denied
ssh deploy@<ip> 'sshd -T | grep -E "permitrootlogin|passwordauthentication"'
# expect: permitrootlogin no / passwordauthentication no
hcloud firewall describe app-edge              # expect: only 22 (scoped)/80/443 inbound
ssh deploy@<ip> 'ss -tlnp'                      # expect: only expected listeners
```

To lint a cloud-init / hardening file before you ever create the box, run
`scripts/verify.sh path/to/cloud-init.yaml` — it statically checks for the
must-haves (no root login, no password auth, an SSH key, a firewall step,
fail2ban) with no network calls.
Pliki w paczce (6)
  • SKILL.md 10 KB
  • evals/README.md 2 KB
  • evals/cases.yaml 4 KB
  • references/cloud-init.md 4 KB
  • references/plans-and-locations.md 3 KB
  • scripts/verify.sh 4 KB

Wersje

RepozytoriumWynikLicencja
Ta strona · ericrisco/rsc-harness100MIT
evolv3-ai/vibe-skills100MIT

Migawka bazy z 28.10.2026 · wersja paczki 49564e479ef3 (skrót SHA-256 całej paczki)

Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.

Podobne skille

  • 100hetzner-cloud
    Zarządza infrastrukturą Hetzner Cloud z terminala przez hcloud CLI: serwery, firewalle, sieci, wolumeny, snapshoty i klucze SSH.
  • 90hetzner-cloud
    Pomaga w usługach Hetzner: serwery, sieci, zapory, load balancery, pamięć Storage Box i klastry Kubernetes.
  • 90hetzner-cloud
    Zarządza infrastrukturą Hetzner Cloud przez CLI hcloud: serwery, sieci, zapory, wolumeny, DNS i certyfikaty.
  • 95hetzner-deploy
    Obsługuje infrastrukturę Hetzner Cloud przez CLI hcloud: serwery, sieci, zapory, load balancery, strefy DNS i wolumeny oraz wdrożenia na nich.
  • 100coolify
    Wdraża aplikacje, bazy danych i usługi na Coolify oraz zarządza nimi: logi wdrożeń, zmienne środowiskowe, serwery i rozwiązywanie problemów, przez Coolify CLI i REST API.
  • 100deploy-to-hetzner
    Stawia usługę kontenerową na Hetzner Cloud przez OpenTofu: VM na środowisko, cloud-init, Docker Compose z Traefik TLS, wdrożenia po digeście obrazu i backupy z próbą odtworzenia.

Uwagi i zgłoszenia

Wiadomość trafi do Bartosza Sokolińskiego. Adres e-mail jest potrzebny tylko do odpowiedzi. Szczegóły w polityce prywatności.