64 lines
2.3 KiB
Markdown
64 lines
2.3 KiB
Markdown
# Watchtower Observability Stack
|
|
|
|
GDPR-compliant, multi-tenant observability stack (metrics, logs, traces) running on a
|
|
single Hetzner CPX42 in Nuremberg (nbg1). Built with Mimir, Loki, Tempo, and Grafana.
|
|
|
|
- **Infrastructure:** OpenTofu (state in Cloudflare R2)
|
|
- **Server config:** Ansible
|
|
- **Container runtime:** Podman Quadlets on AlmaLinux 10
|
|
- **Object storage:** Hetzner Object Storage (S3-compatible, EU-resident)
|
|
- **Access:** WireGuard VPN only — no public service ports
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
# 1. Provision infrastructure
|
|
cd tofu
|
|
cp backend.hcl.example backend.hcl # set your Cloudflare Account ID
|
|
cp terraform.tfvars.example terraform.tfvars # set Hetzner tokens + SSH key
|
|
set -a && source .env && set +a # load R2 credentials into env
|
|
tofu init -backend-config=backend.hcl
|
|
tofu apply
|
|
|
|
# 2. Configure server
|
|
cd ../ansible
|
|
cp inventory/hosts.yml.example inventory/hosts.yml # set host from tofu output
|
|
ansible-playbook -i inventory/hosts.yml playbooks/site.yml --ask-vault-pass
|
|
|
|
# 3. Onboard your first tenant
|
|
ansible-playbook playbooks/onboard-tenant.yml -e tenant_slug=team-alpha
|
|
```
|
|
|
|
## Repository layout
|
|
|
|
| Path | Purpose |
|
|
|------|---------|
|
|
| `tofu/` | OpenTofu infrastructure (Hetzner Cloud + Object Storage, Cloudflare R2 state) |
|
|
| `ansible/` | Server provisioning roles and playbooks |
|
|
| `quadlets/` | Podman Quadlet unit files (deployed by Ansible) |
|
|
| `config/` | Mimir, Loki, Tempo, Grafana configuration templates |
|
|
| `wireguard/` | WireGuard server template (peers vaulted in Ansible) |
|
|
| `scripts/` | Operational scripts (gdpr-purge, onboard-tenant, etc.) |
|
|
| `docs/` | Architecture, GDPR, multi-tenancy, runbook |
|
|
|
|
## Documentation
|
|
|
|
- [Architecture](docs/architecture.md)
|
|
- [GDPR compliance](docs/gdpr-compliance.md)
|
|
- [Multi-tenancy](docs/multi-tenancy.md)
|
|
- [Runbook](docs/runbook.md)
|
|
|
|
## Required secrets
|
|
|
|
| Secret | Where | Stored as |
|
|
|--------|-------|-----------|
|
|
| Hetzner Cloud API token | OpenTofu | `terraform.tfvars` (gitignored) |
|
|
| Hetzner Object Storage S3 keys | OpenTofu + Ansible | `terraform.tfvars` + Ansible vault |
|
|
| Cloudflare R2 access key/secret | OpenTofu backend | env vars `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` |
|
|
| WireGuard server private key | Ansible | Ansible vault |
|
|
| Grafana admin password | Ansible | Ansible vault |
|
|
|
|
## License
|
|
|
|
See [LICENSE](LICENSE).
|