# 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.
