# Homelab — k3s Cluster 2-node k3s cluster (1 manager, 1 worker) running a self-hosted homelab stack. ## Architecture ``` Manager Node Worker Node ┌──────────────┐ ┌──────────────┐ │ k3s server │────────────│ k3s agent │ │ Traefik │ │ │ │ Longhorn │ │ Longhorn │ └──────┬───────┘ └──────┬───────┘ │ │ └───────── NFS ─────────────┘ /dogstore ``` **Storage strategy:** - `/dogstore` (NFS) — mounted on both nodes, used via hostPath for large media/data volumes - Longhorn — replicated block storage for small config/state volumes **Ingress:** k3s built-in Traefik with Let's Encrypt via Cloudflare DNS challenge. **Secrets:** SOPS + age encryption. Secrets live in `secrets/secrets.enc.yaml`, encrypted at rest. ## Services | Chart | Namespace | Services | | -------------- | ----------- | -------------------------------------------------------------- | | traefik-config | kube-system | Traefik HelmChartConfig overlay | | media | media | Plex, Sonarr, Radarr, Bazarr, Prowlarr, qBittorrent, unpackerr | | paperless | paperless | Paperless-ngx, Redis, PostgreSQL | | mealie | apps | Mealie | | dashboards | apps | Homepage, Glance | | utils | apps | Zerobyte, Seerr | ## Prerequisites - Two Linux machines with NFS `/dogstore` mounted on both - `curl`, `helm`, `kubectl`, `sops`, `age` installed ## Bootstrap ### 1. Install k3s server (manager node) ```bash ./scripts/bootstrap.sh server ``` This prints the worker join command at the end. ### 2. Install k3s agent (worker node) ```bash K3S_URL="https://:6443" K3S_TOKEN="" ./scripts/bootstrap.sh agent ``` ### 3. Install Longhorn ```bash ./scripts/bootstrap.sh longhorn ``` ### 4. Set up SOPS encryption Generate an age keypair (run on each node): ```bash ./scripts/bootstrap.sh sops-keygen ``` Copy the public key into `.sops.yaml`, replacing the placeholder. Then encrypt your secrets: ```bash # Edit secrets/secrets.enc.yaml — replace REPLACE_WITH_* placeholders with real values sops -e -i secrets/secrets.enc.yaml ``` ### 5. Apply secrets ```bash ./scripts/bootstrap.sh apply-secrets ``` ### 6. Deploy all charts ```bash ./scripts/bootstrap.sh deploy ``` Or deploy individually: ```bash kubectl create namespace media helm upgrade --install media charts/media -n media kubectl create namespace paperless helm upgrade --install paperless charts/paperless -n paperless kubectl create namespace apps helm upgrade --install mealie charts/mealie -n apps helm upgrade --install dashboards charts/dashboards -n apps helm upgrade --install utils charts/utils -n apps # Traefik config goes in kube-system (managed by k3s) helm upgrade --install traefik-config charts/traefik-config -n kube-system ``` ## Verifying ```bash # Check all pods kubectl get pods -A # Check ingress routes kubectl get ingress -A # Test a specific service curl -I https://mealie.ratboo.me ``` ## Secret Rotation 1. Decrypt: `sops secrets/secrets.enc.yaml` (opens in `$EDITOR`) 2. Change the values 3. Save and close (SOPS re-encrypts automatically) 4. Apply: `./scripts/bootstrap.sh apply-secrets` 5. Restart affected pods: `kubectl rollout restart deployment/ -n ` ## Repo Structure ``` homelab/ ├── README.md ├── .sops.yaml ├── scripts/ │ └── bootstrap.sh ├── charts/ │ ├── traefik-config/ │ ├── media/ │ ├── paperless/ │ ├── mealie/ │ ├── dashboards/ │ └── utils/ └── secrets/ └── secrets.enc.yaml ```