Initial: Iron Legion Homelab Services Stack PRD

Verifies 16 DockerHub images, assigns target nodes per locked policy,
defines 3-phase deployment order (Infra → Media → Polish).

Domain: *.ai.home
No public internet exposure.
Services: Traefik, Technitium DNS, AdGuard Home, Prometheus, Grafana,
Beszel, Dozzle, Portainer, Homepage, Authelia, Vaultwarden, Jellyfin,
Sonarr, Radarr, Prowlarr, Nextcloud
This commit is contained in:
2026-05-25 17:25:40 -04:00
commit 4cff1b5e48
11 changed files with 826 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
# Iron Legion Homelab Services Stack — Data & Persistence
## Volume Strategy
Every service with persistent state uses **bind mounts to on-node directories**. No named volumes, no NFS, no distributed storage.
## Directory Convention
```
/opt/iron-legion/
├── service-name/
│ ├── data/ # Application data (databases, config, state)
│ ├── config/ # Static config files mounted read-only where possible
│ └── logs/ # Log output (optional, if not sent to stdout)
```
## Per-Service Persistence
| Service | Data Path | Backup Target | Size Estimate |
|---------|-----------|---------------|---------------|
| **Traefik** | `/opt/iron-legion/traefik/config/` `/opt/iron-legion/traefik/certs/` | Bones (daily rsync) | < 50 MB |
| **Technitium DNS** | `/opt/iron-legion/technitium/config/` | Bones | < 10 MB |
| **Pi-hole** | `/opt/iron-legion/pihole/etc-pihole/` `/opt/iron-legion/pihole/etc-dnsmasq.d/` | Bones | < 500 MB |
| **Prometheus** | `/opt/iron-legion/prometheus/data/` | Bones (retention: 15d local, 90d backup) | 520 GB |
| **Grafana** | `/opt/iron-legion/grafana/data/` | Bones | < 500 MB |
| **Beszel** | `/opt/iron-legion/beszel/data/` | Bones | < 1 GB |
| **Portainer** | `/opt/iron-legion/portainer/data/` | Bones | < 100 MB |
| **Homepage** | `/opt/iron-legion/homepage/config/` | Bones | < 10 MB |
| **Vaultwarden** | `/opt/iron-legion/vaultwarden/data/` | Bones (encrypted) | < 500 MB |
| **Authelia** | `/opt/iron-legion/authelia/config/` | Bones | < 10 MB |
| **Jellyfin** | `/opt/iron-legion/jellyfin/config/` `/opt/iron-legion/jellyfin/media/` | **None** (media too large) | < 1 GB config; media drive separate |
| **Sonarr** | `/opt/iron-legion/sonarr/config/` | Bones | < 1 GB |
| **Radarr** | `/opt/iron-legion/radarr/config/` | Bones | < 1 GB |
| **Prowlarr** | `/opt/iron-legion/prowlarr/config/` | Bones | < 100 MB |
| **Nextcloud** | `/opt/iron-legion/nextcloud/data/` | Bones (snapshots) | 1050 GB |
## Media Storage Exception
- **Jellyfin media** lives on a separate mount (likely external USB/NVMe on Mark44). Not backed up via rsync.
- **Sonarr/Radarr** download staging to a shared `/downloads` bind mount, then hardlink/copy to Jellyfin media library.
## Backup Tooling
- **Primary:** `rsync -a --delete` to Bones secondary storage daily at 03:00 local.
- **Vaultwarden:** `rsqlite3` dump + `rsync` (encrypted at rest on Bones).
- **Prometheus:** `snapshot API` → rsync (not raw WAL files).
## Secret Management
- `.env` files live in `/opt/iron-legion/service-name/.env`, mode `0600`.
- Compose files use `${VAR_NAME}` syntax, never literal strings.
- Vaultwarden stores shared secrets (DB passwords, API keys). Artemis holds no secrets in memory.