humanize README: intro, design notes, remove mechanical formatting

This commit is contained in:
Tuan-Dat Tran
2026-04-28 18:52:33 +02:00
parent a187b648e7
commit 3ac7d91101

View File

@@ -1,6 +1,6 @@
# Homelab
A production-grade homelab running on bare-metal Proxmox, with a 17-node Kubernetes cluster managed entirely through GitOps.
17-node Kubernetes cluster on five bare-metal Proxmox hosts, provisioned with Terraform and Ansible, managed through ArgoCD GitOps. Runs my home automation, media stack, photo backup, documents, and a few side projects.
![k3s](https://img.shields.io/badge/k3s-v1.34-orange?logo=kubernetes)
![nodes](https://img.shields.io/badge/nodes-17-blue)
@@ -84,7 +84,7 @@ graph TB
| VM | `docker-lb` | Caddy reverse proxy (LAN only) | 1 vCPU · 2 GB RAM |
| VPS | `mii` | Edge node (Netcup) | WireGuard · Traefik · Pangolin |
All VMs run **Debian 12** on `virtio` network bridges, provisioned from cloud-init templates via **Terraform + Ansible**.
All VMs run Debian 12 on `virtio` network bridges, provisioned from cloud-init templates via Terraform + Ansible.
---
@@ -133,38 +133,38 @@ All VMs run **Debian 12** on `virtio` network bridges, provisioned from cloud-in
---
## Key Design Decisions
## Design notes
**GitOps end-to-end.** Every cluster resource is declared in Git and applied by ArgoCD. Nothing is `kubectl apply`'d by hand. ArgoCD Image Updater closes the loop by writing image tag updates back to Git automatically.
Everything goes through Git. ArgoCD owns the cluster state; nothing gets `kubectl apply`'d directly. ArgoCD Image Updater handles the image update loop: when a new tag appears in the registry, it commits the change back to Git and ArgoCD picks it up from there.
**Secrets in Git, safely.** Sealed Secrets lets encrypted `SealedSecret` manifests live in the same repo as everything else. Only the in-cluster controller can decrypt them.
Secrets are committed to Git too, encrypted via Sealed Secrets. Only the in-cluster controller holds the decryption key.
**No cloud dependency for ingress.** MetalLB + Traefik handles all internal load balancing. External access goes through Cloudflare tunnels or a WireGuard VPN — no ports open on the home router.
No ports are open on the home router. Internal load balancing goes through MetalLB + Traefik. External access uses Cloudflare tunnels or a WireGuard VPN routed through the edge VPS.
**Distributed storage without a SAN.** Longhorn replicates volumes across all 14 agent nodes. NFS on a dedicated bare-metal host serves the media library to Jellyfin with low latency.
Longhorn handles block storage by replicating volumes across all 14 agent nodes. The media library lives on a dedicated NFS host instead — latency matters when Jellyfin is reading large video files, and NFS is simpler for that.
**Observability from day one.** Prometheus + Grafana for metrics, Elastic Stack (via ECK operator) for logs and fleet management. Elastic Agents run as a DaemonSet across the whole cluster.
Metrics go to Prometheus + Grafana. Logs and fleet management go to Elastic Stack via the ECK operator, with Elastic Agents running as a DaemonSet so every node is covered.
**Provisioning is reproducible.** Proxmox VMs are created via Terraform (Proxmox provider), then configured by Ansible roles — from base OS hardening to k3s installation and kubeconfig management.
All VMs are provisioned with Terraform and configured by Ansible. Rebuilding from scratch doesn't require remembering anything.
---
## Repository Layout
## Repo layout
```
ansible-homelab/ # Ansible roles + playbooks for all VM provisioning
ansible-homelab/
├── roles/
│ ├── common/ # Base OS config, SSH hardening, node-exporter
│ ├── k3s_server/ # HA control plane install + taint config
│ ├── k3s_agent/ # Worker node install
│ ├── kube_vip/ # HA VIP (kube-vip DaemonSet on control plane nodes)
│ ├── docker_host/ # Docker + GPU passthrough
│ ├── proxmox/ # Proxmox node config
│ └── edge_vps/ # VPS services (WireGuard, Traefik, Pangolin)
└── playbooks/ # Top-level playbooks per host group
│ ├── common/ # base OS config, SSH hardening, node-exporter
│ ├── k3s_server/ # control plane install + NoSchedule taint
│ ├── k3s_agent/ # worker node install
│ ├── kube_vip/ # kube-vip DaemonSet + TLS SAN config
│ ├── docker_host/ # Docker + Intel QuickSync GPU passthrough
│ ├── proxmox/ # Proxmox node setup
│ └── edge_vps/ # VPS: WireGuard, Traefik, Pangolin, Elastic Agent
└── playbooks/
argocd-homelab/ # All Kubernetes manifests (ArgoCD App-of-Apps)
├── infrastructure/ # Platform: MetalLB, Longhorn, Cert-Manager, ECK,
├── services/ # Applications: Immich, Vaultwarden, arr-stack,
└── cluster-apps/ # ArgoCD ApplicationSets + root app
argocd-homelab/
├── infrastructure/ # MetalLB, Longhorn, Cert-Manager, ECK, Istio, ...
├── services/ # Immich, Vaultwarden, arr-stack, Home Assistant, ...
└── cluster-apps/ # ArgoCD App-of-Apps root + ApplicationSets
```