Architecture: Hetzner Edge VPS
## Deployment: Hetzner Edge VPS
Hetzner CAX11 (cpx11) VPS at 178.156.129.142 serving as the internet-facing edge proxy and mail relay for the pal-e platform.
### Diagram
```mermaid
graph TB
subgraph Internet
DNS[DNS A Records]
Client[External Clients]
end
subgraph Hetzner["Hetzner Edge VPS (178.156.129.142)"]
Caddy[Caddy :80/:443]
Stalwart[Stalwart :25/:587/:993/:8080]
TS_Edge[Tailscale Agent]
end
subgraph Home["Home k3s Cluster"]
TS_K3s[Tailscale Agent]
Apps[Application Pods]
end
DNS --> Client
Client -->|HTTPS| Caddy
Client -->|SMTP| Stalwart
Caddy -->|reverse proxy| TS_Edge
TS_Edge -->|mesh VPN| TS_K3s
TS_K3s --> Apps
Stalwart -->|submission| Apps
```
### Components
| Component | Purpose | Notes |
|-----------|---------|-------|
| Caddy | Reverse proxy + TLS termination | Ports 80, 443; auto-HTTPS via ACME |
| Stalwart | SMTP relay, submission, IMAP, admin UI | Ports 25, 587, 993, 8080 |
| Tailscale | Mesh VPN to k3s cluster | Connects edge to home network |
| Hetzner Firewall | Cloud-level packet filtering | Ports 22, 80, 443, 25, 587, 993 |
### Key Decisions
- **Edge VPS instead of k3s for mail**: Home ISPs block port 25 outbound; Hetzner allows it after unblock request
- **PTR/rDNS on Hetzner IP**: Required for mail deliverability; only available on static IPs with hosting provider control
- **Public IP stability**: Hetzner provides a fixed IPv4 that DNS A records point to — no DDNS needed
- **SaltStack for config management**: Declarative state for Caddy + Stalwart; pillar-driven config
- **OpenTofu for provisioning**: Server, firewall, SSH key managed as IaC
### Related
- `arch-dns` — DNS records pointing to this VPS
- `arch-stalwart` — Stalwart mail server running on this VPS
- Project: `stalwart-email`
## Management
- **Provisioning**: OpenTofu (`terraform/modules/hetzner-edge/main.tf`)
- **Configuration**: SaltStack (`salt/states/top.sls` → `edge-proxy` minion)
- **Firewall**: Hetzner cloud firewall (ports 22, 80, 443) + port 25/587/993 for mail
- **Connectivity**: Tailscale for internal cluster access
## Key Files
- `terraform/modules/hetzner-edge/main.tf` — server, firewall, SSH key
- `salt/states/caddy/` — Caddy state + Caddyfile.j2 template
- `salt/states/top.sls` — assigns states to `edge-proxy` minion
- `salt/pillar/caddy.sls` — site definitions (domain + proxy_target)