Files
vaultwarden-deployment/README.md
T
2026-05-25 19:06:03 -05:00

50 lines
2.4 KiB
Markdown

# Vaultwarden self-host deployment
Reproducible, single-user, zero-knowledge Vaultwarden on Hetzner Cloud + AlmaLinux 10
with rootless Podman quadlets, custom Caddy (DNS-01 + Coraza DetectionOnly), restic
backups to Cloudflare R2, and WireGuard-gated admin access.
Plan: `~/.claude/plans/vaultwarden-self-host-sharded-creek.md`.
## Layout
| Path | Purpose |
|---|---|
| `cloudbuild.yaml` | Cloud Build pipeline — drives Pulumi + image build + Ansible |
| `pulumi/` | Phase 1: Hetzner server, Cloud Firewall, volume, GCP Cloud DNS records (Pulumi Go) |
| `caddy/` | Phase 2: custom Caddy build (Containerfile + pinned versions) and the Caddyfile |
| `ansible/` | Phase 3: OS hardening, WireGuard server, nftables, fail2ban, msmtp, dnf-automatic, Secret Manager → Podman secrets |
| `quadlets/` | Phase 4: rootless Vaultwarden + Caddy quadlet units |
| `backup/` | Phase 5: SQLite-snapshot + restic + systemd timer + Healthchecks pings |
| `runbook/` | Deployment runbook, cutover checklist, restore drill |
| `secrets/` | Docs only — secret material lives in Google Cloud Secret Manager, fetched at deploy time |
## Stack
| Layer | Choice |
|---|---|
| Compute | Hetzner Cloud, CX22, AlmaLinux 10 (`alma-10` image) |
| IaC | Pulumi (Go) |
| OS config | Ansible |
| Build / deploy pipeline | Google Cloud Build (image build, Trivy scan, Pulumi, Ansible) |
| Secrets | Google Cloud Secret Manager (OIDC via Cloud Build SA; ADC for local operator) |
| Reverse proxy / TLS / WAF | Caddy (custom: googleclouddns DNS-01 + Coraza DetectionOnly) |
| Network filtering | Hetzner Cloud Firewall + host nftables + Fail2Ban |
| Containers | Rootless Podman quadlets, SELinux enforcing |
| DB | SQLite (single-user; no replication required) |
| Updates | `dnf-automatic` (OS) + `podman auto-update` (containers) |
| Backups | restic → Cloudflare R2 |
| DNS | GCP Cloud DNS |
| Admin/SSH gating | WireGuard server on the Vaultwarden host |
| Alerting | Healthchecks.io (timer pings) + msmtp email (`systemd OnFailure=`) |
## Build-it-disposably-first
Two Pulumi stacks: `disposable` (throwaway hostname, no real vault data) and `production`.
Iterate `pulumi destroy && pulumi up` on `disposable` until the stack stands up cleanly
with zero hand-intervention, then promote to `production` for migration.
## Pinned versions
See `memory/coraza_caddy_version.md` for the Coraza/Caddy version pinning rationale.