mcpField
H
hetzner-server

hetzner-server

github.com/connoradsCloud & DevOps18233 installsUpdated 2d ago
Repository

About hetzner-server

hetzner-server is a skill for creating and managing Hetzner Cloud servers with the hcloud CLI. It is aimed at operators handling provisioning and maintenance, with guidance on firewalls, initialization, SSH configuration, and access.

Key features

  1. 01Create servers with the hcloud CLI
  2. 02Apply reusable firewall profiles at creation
  3. 03Run setup scripts through cloud-init
  4. 04Configure swap and check installation progress
  5. 05Sync SSH configuration for servers
  6. 06Clone private repositories with SSH agent forwarding

Use cases

  1. 01Create a Hetzner Cloud server with an initialization script
  2. 02Change firewall rules after server creation
  3. 03Configure swap and verify server status
  4. 04Access private GitHub repositories using forwarded SSH agents

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/connorads/dotfiles --skill hetzner-server

After installing, type/hetzner-serverin Claude Code to confirm it loaded.

Details

From the project README

Hetzner Server Management

Create and manage Hetzner Cloud servers using the hcloud CLI.

Prerequisites

  • hcloud CLI installed (via mise: hcloud = "latest")
  • Authenticated: hcloud context create <name> with API token from https://console.hetzner.cloud

Cloud Firewalls

Reusable firewall profiles applied at server creation. Firewalls can be swapped on running servers - use apply-to-resource / remove-from-resource.

FirewallRulesUse case
ts-sshUDP 41641 (Tailscale) + TCP 22 (SSH)Dev boxes - initial setup, swap to ts-only after restricting UFW to Tailscale
ts-onlyUDP 41641 (Tailscale)Tailscale-only access, no public ports
ts-webUDP 41641 (Tailscale) + TCP 80,443 (HTTP/S)Servers accepting public web traffic
Swapping firewalls on a running server
hcloud firewall remove-from-resource ts-ssh --type server --server dev
hcloud firewall apply-to-resource ts-only --type server --server dev

Quick Reference

Create a server
# Prefer ARM (best value)
hcloud server create \
  --name dev \
  --type cax21 \
  --image ubuntu-24.04 \
  --location nbg1 \
  --ssh-key connorads \
  --ssh-key connor@penguin \
  --firewall ts-ssh

# x86 fallback
hcloud server create \
  --name dev \
  --type cpx21 \
  --image ubuntu-24.04 \
  --location nbg1 \
  --ssh-key connorads \
  --ssh-key connor@penguin \
  --firewall ts-ssh

# IPv6-only (saves ~$0.60/month on IPv4)
hcloud server create \
  --name dev \
  --type cax21 \
  --image ubuntu-24.04 \
  --location nbg1 \
  --ssh-key connorads \
  --ssh-key connor@penguin \
  --firewall ts-ssh \
  --without-ipv4

IPv6-only caveat: hcloud server ip <name> and hcssh assume IPv4 - server ip prints nothing useful and hcssh reads only the ipv4 column, so the managed Host entries come out empty. For a --without-ipv4 server use hcloud server ip --ipv6 <name> (returns the /64; the host is ...::1) and write the SSH Host entry by hand, or just reach it over Tailscale once ts up has run.

With user-data (auto-run install script)
# Use heredoc - process substitution <(echo '...') escapes the shebang incorrectly
hcloud server create \
  --name dev \
  --type cax21 \
  --image ubuntu-24.04 \
  --location nbg1 \
  --ssh-key connorads \
  --ssh-key connor@penguin \
  --firewall ts-ssh \
  --user-data-from-file - <<'EOF'
#!/bin/bash
curl -fsSL https://raw.githubusercontent.com/connorads/dotfiles/master/install.sh | bash
EOF

The dotfiles installation takes ~5 minutes. To monitor progress:

# Quick status check
ssh connor@$(hcloud server ip dev) "cloud-init status"

# View recent installation logs
ssh connor@$(hcloud server ip dev) "sudo journalctl -u cloud-final -n 50 --no-pager"

# Follow installation in real-time
ssh connor@$(hcloud server ip dev) "sudo journalctl -u cloud-final -f"

# Check if tools are installed
ssh connor@$(hcloud server ip dev) "which zsh mise && echo \$SHELL"
With swap (recommended for production)

Ubuntu cloud images don't include swap by default. Add swap via cloud-init at creation:

# Create server with 16GB swap (1:1 ratio for 16GB RAM server)
hcloud server create \
  --name dev \
  --type cax31 \
  --image ubuntu-24.04 \
  --location nbg1 \
  --ssh-key connorads \
  --ssh-key connor@penguin \
  --firewall ts-ssh \
  --user-data-from-file - <<'EOF'
#cloud-config
swap:
  filename: /swapfile
  size: 16G
  maxsize: 16G
EOF

Recommended swap sizes:

  • 4GB RAM → 4-8GB swap
  • 8GB RAM → 8GB swap
  • 16GB+ RAM → 16GB swap (1:1 ratio)

Add swap to existing server:

# Create 16GB swap file
ssh connor@$(hcloud server ip dev) "sudo fallocate -l 16G /swapfile && \
  sudo chmod 600 /swapfile && \
  sudo mkswap /swapfile && \
  sudo swapon /swapfile && \
  echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab"

# Verify swap is active
ssh connor@$(hcloud server ip dev) "free -h"
Reference lookups

Server types, locations, images, SSH keys, and day-to-day server ops are live queries - look them up, don't memorise:

hcloud server-type list                              # ARM cax* preferred (best value); cpx* x86 fallback
hcloud location list                                 # nbg1 is the usual default here
hcloud image list --type system --architecture arm
hcloud ssh-key list
hcloud server list|ip|delete|poweroff|poweron|reboot|rebuild   # rebuild keeps the IP

Cloning GitHub repos (SSH agent forwarding)

Use the <name>-agent SSH host (which has agent forwarding enabled) to clone private repos without copying keys to the server. If you hit host key errors, add GitHub's host key first.

# First time only: add GitHub's host key
ssh dev "ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null"

# Confirm forwarded agent is visible
ssh dev-agent "ssh-add -l"

# Clone with agent forwarding (use -agent suffix)
ssh dev-agent "mkdir -p ~/git && cd ~/git && git clone [email protected]:you/repo.git"

# Clone specific branch
ssh dev-agent "mkdir -p ~/git && cd ~/git && git clone [email protected]:you/repo.git && cd repo && git checkout branch-name"

# Push/pull with agent forwarding
ssh dev-agent "cd repo && git push"

For interactive sessions (e.g., lazygit):

ssh dev-agent
# Then on server: git clone/push/pull works with forwarded agent

Post-creation setup

After creating a server, always clear any old host keys for that IP (Hetzner reuses IPs):

ssh-keygen -R $(hcloud server ip dev) 2>/dev/null
ssh-keyscan $(hcloud server ip dev) >> ~/.ssh/known_hosts 2>/dev/null

Then generate/update SSH config entries:

hcssh              # update ~/.ssh/config with all Hetzner servers
hcssh --dry-run    # preview without writing

This creates two Host entries per server inside a managed block (# BEGIN/END hetzner-managed):

  • <name> - no agent forwarding (safe for AI agents)
  • <name>-agent - with agent forwarding (for git push/pull to GitHub)

Run hcssh again after creating/deleting servers to keep SSH config in sync. This enables VS Code Remote-SSH to show the server in the dropdown.

Optional: Restrict SSH to Tailscale only

After ts up and confirming SSH works via Tailscale (ts ssh connor@dev), run these commands on the server to restrict SSH to the Tailscale interface:

sudo ufw delete allow 22/tcp
sudo ufw allow in on tailscale0 to any port 22 proto tcp comment 'SSH via Tailscale'
sudo ufw reload

Fallback: Hetzner Cloud Console VNC if locked out.

Notes

  • IPv6-only saves money but requires Tailscale/cloudflared for access from IPv4 networks
  • User-data runs as root on first boot
  • The dotfiles install.sh handles creating user connor, installing Nix, home-manager, and mise tools

Comments

Markdown supported