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
/dogstoremounted on both curl,helm,kubectl,sops,ageinstalled
Bootstrap
1. Install k3s server (manager node)
./scripts/bootstrap.sh server
This prints the worker join command at the end.
2. Install k3s agent (worker node)
K3S_URL="https://<manager-ip>:6443" K3S_TOKEN="<token>" ./scripts/bootstrap.sh agent
3. Install Longhorn
./scripts/bootstrap.sh longhorn
4. Set up SOPS encryption
Generate an age keypair (run on each node):
./scripts/bootstrap.sh sops-keygen
Copy the public key into .sops.yaml, replacing the placeholder. Then encrypt your secrets:
# Edit secrets/secrets.enc.yaml — replace REPLACE_WITH_* placeholders with real values
sops -e -i secrets/secrets.enc.yaml
5. Apply secrets
./scripts/bootstrap.sh apply-secrets
6. Deploy all charts
./scripts/bootstrap.sh deploy
Or deploy individually:
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
# 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
- Decrypt:
sops secrets/secrets.enc.yaml(opens in$EDITOR) - Change the values
- Save and close (SOPS re-encrypts automatically)
- Apply:
./scripts/bootstrap.sh apply-secrets - Restart affected pods:
kubectl rollout restart deployment/<name> -n <namespace>
Repo Structure
homelab/
├── README.md
├── .sops.yaml
├── scripts/
│ └── bootstrap.sh
├── charts/
│ ├── traefik-config/
│ ├── media/
│ ├── paperless/
│ ├── mealie/
│ ├── dashboards/
│ └── utils/
└── secrets/
└── secrets.enc.yaml
Description
Languages
Shell
94%
Smarty
6%