Files

62 lines
2.7 KiB
Markdown

# Hetzner Snapshot Automation
Weekly snapshots of all running servers in a Hetzner Cloud account. Part of the provider-level backup layer (layer 1 in the three-layer model).
## Script
File: `snapshot-hetzner.py` at `/root/.hermes/scripts/snapshot-hetzner.py`
Reads the API token from a companion file `.hetzner_token` in the same directory (avoids shell escaping issues with special characters in the token).
## API Token Handling
The Hetzner Cloud API token can contain special characters (`$`, `!`, etc.) that break shell-based environment variable passing. **Do not use `export TOKEN=...` in bash** — the shell will mangle it.
**Pattern:** Store the token in a dedicated file, have the script read it directly:
```python
script_dir = os.path.dirname(os.path.abspath(__file__))
token_file = os.path.join(script_dir, ".hetzner_token")
with open(token_file) as f:
TOKEN = f.read().strip()
```
Token file permissions: `chmod 600`.
## Setup
1. Generate a Hetzner Cloud API token (Read & Write) from the Hetzner console
2. Write it to `/root/.hermes/scripts/.hetzner_token` (one line, no trailing newline via Python write)
3. `chmod 600 /root/.hermes/scripts/.hetzner_token`
4. Test: `python3 /root/.hermes/scripts/snapshot-hetzner.py`
5. Cron job handles weekly runs (Monday at 5:00 UTC = 1:00 AM ET)
## What it does
- Lists all servers in the account via GET `/v1/servers`
- For each running server, POSTs a `create_image` action with `type=snapshot`
- Snapshot description: `auto-weekly-{server-name}-{date}`
- Skips stopped/offline servers
## Cron schedule
```
0 5 * * 1 (Mondays at 5:00 UTC / 1:00 AM ET)
```
Via no_agent cron job, silent output (watchdog pattern).
## Pitfalls
- **Token expires** — Hetzner tokens can have expiry dates. If snapshots start failing with 401, regenerate the token and update the file.
- **Writing the token file via echo truncates it** — Bash `echo "$TOKEN" > file` can silently truncate tokens containing `$`, `!`, backticks, or other shell-special characters. The file may contain fewer characters than the actual token, causing silent auth failures. Always write the token via Python:
```python
token = 'paste-the-full-token-here'
with open('/path/to/.hetzner_token', 'w') as f:
f.write(token)
print(f'Wrote {len(token)} chars')
```
Verify the length matches the token in the Hetzner console.
- **Snapshot names are unique** — A snapshot with description `auto-weekly-{name}-{date}` allows easy identification in the Hetzner console. Hetzner allows up to 10 snapshots per server.
- **Billing** — Hetzner charges for snapshot storage (based on disk size). CPX11 (40GB) snapshots cost ~$0.01-0.02 each. Full account backup of 9 servers = negligible monthly cost.