{"id":50440784,"url":"https://github.com/vineethvijay/prox-k8s-lab","last_synced_at":"2026-05-31T19:01:50.211Z","repository":{"id":349718905,"uuid":"1202690656","full_name":"vineethvijay/prox-k8s-lab","owner":"vineethvijay","description":"A Kubernetes homelab on Proxmox VMs — provisioned with Ansible, managed with ArgoCD and Helm","archived":false,"fork":false,"pushed_at":"2026-04-07T07:44:00.000Z","size":3831,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-07T09:28:54.678Z","etag":null,"topics":["ansible","argocd","automation","devops","docker","gitops","gpu-passthrough","helm","high-availability","home-server","homelab","infrastructure-as-code","jellyfin","kubeadm","kubernetes","linux","media-server","plex","proxmox","self-hosted"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vineethvijay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-06T09:44:17.000Z","updated_at":"2026-04-07T07:44:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vineethvijay/prox-k8s-lab","commit_stats":null,"previous_names":["vineethvijay/prox-k8s-lab"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/vineethvijay/prox-k8s-lab","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vineethvijay%2Fprox-k8s-lab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vineethvijay%2Fprox-k8s-lab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vineethvijay%2Fprox-k8s-lab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vineethvijay%2Fprox-k8s-lab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vineethvijay","download_url":"https://codeload.github.com/vineethvijay/prox-k8s-lab/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vineethvijay%2Fprox-k8s-lab/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33744447,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","argocd","automation","devops","docker","gitops","gpu-passthrough","helm","high-availability","home-server","homelab","infrastructure-as-code","jellyfin","kubeadm","kubernetes","linux","media-server","plex","proxmox","self-hosted"],"created_at":"2026-05-31T19:01:48.908Z","updated_at":"2026-05-31T19:01:50.205Z","avatar_url":"https://github.com/vineethvijay.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Proxmox Kubernetes Homelab\n\nA complete, code-driven homelab that takes two bare-metal Proxmox hosts from empty to a fully operational HA Kubernetes cluster running self-hosted services — media streaming, automation, DNS, backups, monitoring, and more.\n\nAnsible provisions the VMs and bootstraps the cluster, kubeadm sets up a highly available control plane, and ArgoCD takes over from there — continuously deploying and self-healing every application via GitOps using local Helm charts. Push to `main`, everything syncs. This repo is the single source of truth.\n\n\u003e **Disclaimer:** This project is for **homelab learning and educational purposes** only. I do not support or encourage piracy. The media automation tools included here are meant to be used with legally obtained content.\n\n## How It All Works\n\nThis project automates a complete Kubernetes homelab from bare metal to running services. The pipeline flows through four stages: **Ansible provisions VMs on Proxmox**, **kubeadm bootstraps an HA K8s cluster**, **ArgoCD takes over for continuous GitOps delivery**, and **a bunch of self-healing applications** run media automation, streaming, DNS, backups, and more — all driven from this single repository.\n\n### End-to-End Architecture\n\n![Architecture Animation](docs/architecture-demo.gif)\n\n\u003e **[View Interactive Version](https://vineethvijay.github.io/prox-k8s-lab/docs/architecture-animation-v2.html)** — best viewed in a desktop browser\n\n#### Diagram \n\n```mermaid\nflowchart TB\n    GH[\"fa:fa-code-branch GitHub Repository\u003cbr/\u003evineethvijay/prox-k8s-lab\"]:::git\n\n    GH --\u003e|\"ansible/\u003cbr/\u003eInfrastructure as Code\"| ANSIBLE[\"fa:fa-cogs Ansible\u003cbr/\u003e13-Phase Pipeline\"]:::ansible\n    GH -.-\u003e|\"argocd/ + helm/\u003cbr/\u003eGitOps auto-sync on main\"| ARGO\n\n    ANSIBLE --\u003e|\"Phase 1: cloud-init\u003cbr/\u003eVM creation\"| PROXMOX\n\n    subgraph PROXMOX[\"PROXMOX VE HYPERVISORS\"]\n        subgraph PVE1[\"pve-local · 192.168.1.11\u003cbr/\u003eIntel i7-9750H · 16GB\"]\n            CP1[\"k8s-cp · .200\u003cbr/\u003eControl Plane · 2c/3GB\"]:::cp\n            W1[\"k8s-w1 · .201\u003cbr/\u003eWorker · 3c/4GB\"]:::worker\n            W2[\"k8s-w2 · .202\u003cbr/\u003eWorker · 4c/6GB\u003cbr/\u003eiGPU + GTX 1650\"]:::gpu\n        end\n        subgraph PVE2[\"pve-remote · 192.168.1.8\u003cbr/\u003eRyzen 5 5500U · 16GB\"]\n            CP2[\"k8s-cp2 · .205\u003cbr/\u003eControl Plane · 2c/3GB\"]:::cp\n            CP3[\"k8s-cp3 · .206\u003cbr/\u003eControl Plane · 2c/3GB\"]:::cp\n            W4[\"k8s-w4 · .204\u003cbr/\u003eWorker · 4c/6GB\"]:::worker\n        end\n    end\n\n    ANSIBLE --\u003e|\"Phases 2–7\u003cbr/\u003ekubeadm + HA setup\"| K8S\n\n    subgraph K8S[\"KUBERNETES v1.32 — 6-NODE HA CLUSTER\"]\n        VIP[\"kube-vip\u003cbr/\u003eVIP 192.168.1.199\"]:::net\n        CNI[\"Calico CNI\u003cbr/\u003e10.244.0.0/16\"]:::net\n        LB[\"MetalLB L2\u003cbr/\u003ePool .240–.250\"]:::net\n        ING[\"NGINX Ingress\u003cbr/\u003e*.homelab.local\"]:::net\n        ARGO[\"ArgoCD\u003cbr/\u003eApp-of-Apps\"]:::argo\n    end\n\n    ARGO --\u003e|\"auto-sync · self-heal\u003cbr/\u003eprune\"| APPS\n\n    subgraph APPS[\"29 ARGOCD-MANAGED APPLICATIONS\"]\n        subgraph M[\"Media \u0026 Streaming\"]\n            PLEX[\"Plex · .241\u003cbr/\u003eGPU Transcode\"]:::media\n            JELLY[\"Jellyfin\u003cbr/\u003eGPU Transcode\"]:::media\n            ARR[\"Sonarr · Radarr · Lidarr\u003cbr/\u003eReadarr · Bazarr\u003cbr/\u003eProwlarr · Seerr\"]:::media\n            STATS[\"Tautulli · Jellystat\u003cbr/\u003ePinchflat · Random Streamer ✦\"]:::media\n        end\n        subgraph D[\"Downloads\"]\n            SAB[\"SABnzbd\"]:::download\n            QB[\"Downloader\u003cbr/\u003e+ Gluetun VPN\"]:::download\n            FL[\"FlareSolverr\"]:::download\n        end\n        subgraph P[\"Platform \u0026 Infrastructure\"]\n            PI[\"Pi-hole DNS · .242\"]:::infra\n            VW[\"Vaultwarden\"]:::infra\n            DASH[\"Homepage · Headlamp\u003cbr/\u003eGatus · Glances\"]:::infra\n            BK[\"Kopia Backup · Kopia UI\u003cbr/\u003eFilebrowser\"]:::infra\n            NFP[\"NFS Provisioners\u003cbr/\u003eMetalLB Config\u003cbr/\u003eDocker Registry · .245\"]:::infra\n        end\n    end\n\n    subgraph STORAGE[\"EXTERNAL STORAGE\"]\n        NAS[\"Synology NAS · .28\u003cbr/\u003e16TB RAID\"]:::storage\n        HDD[\"Proxmox Local HDD\"]:::storage\n    end\n\n    STORAGE -.-\u003e|\"NFS mounts\u003cbr/\u003eon all workers\"| K8S\n\n    classDef git fill:#6e40c9,stroke:#6e40c9,color:#fff\n    classDef ansible fill:#ee0000,stroke:#cc0000,color:#fff\n    classDef cp fill:#326ce5,stroke:#2457b5,color:#fff\n    classDef worker fill:#4a90d9,stroke:#3a7bc8,color:#fff\n    classDef gpu fill:#76b900,stroke:#5a8f00,color:#fff\n    classDef net fill:#f5a623,stroke:#d4891a,color:#fff\n    classDef argo fill:#ef7b4d,stroke:#d4642e,color:#fff\n    classDef media fill:#e040fb,stroke:#c020d9,color:#fff\n    classDef download fill:#00bcd4,stroke:#0097a7,color:#fff\n    classDef infra fill:#607d8b,stroke:#455a64,color:#fff\n    classDef storage fill:#8bc34a,stroke:#689f38,color:#fff\n```\n\n\u003e **Key takeaway:** This repo is the single source of truth. Ansible handles one-time infrastructure provisioning (VMs, cluster, networking). ArgoCD handles ongoing application delivery — push to `main` and everything auto-deploys.\n\u003e\n\u003e ✦ = My own development\n\n### Provisioning Pipeline\n\nAnsible executes 13 playbooks sequentially to go from bare metal to a fully operational cluster:\n\n```mermaid\nflowchart LR\n    P1[\"01 Create VMs\u003cbr/\u003ecloud-init on Proxmox\"]:::infra\n    P2[\"02 Prepare Nodes\u003cbr/\u003econtainerd · kubeadm\u003cbr/\u003ekubelet\"]:::infra\n    P3[\"03 Init Cluster\u003cbr/\u003ekubeadm init\u003cbr/\u003eCalico CNI\u003cbr/\u003eJoin workers\"]:::k8s\n    P4[\"04 Monitoring\u003cbr/\u003ePrometheus\u003cbr/\u003eGrafana\"]:::k8s\n    P5[\"05 Ingress\u003cbr/\u003eMetalLB\u003cbr/\u003eNGINX\"]:::k8s\n    P6[\"06 Remote Workers\u003cbr/\u003e2nd Proxmox host\"]:::ha\n    P7[\"07 HA Conversion\u003cbr/\u003ekube-vip VIP\u003cbr/\u003e3 control planes\"]:::ha\n    P8[\"08 Longhorn Deps\u003cbr/\u003eopen-iscsi\u003cbr/\u003enfs-common\"]:::storage\n    P9[\"09 NFS Mounts\u003cbr/\u003eNAS media\u003cbr/\u003eHDD · Backups\"]:::storage\n    P10[\"10 DNS Hosts\u003cbr/\u003eMac /etc/hosts\"]:::storage\n    P11[\"11 ArgoCD\u003cbr/\u003eHelm +\u003cbr/\u003eApp-of-Apps\"]:::gitops\n    P12[\"12 Glances\u003cbr/\u003eHost monitoring\"]:::gitops\n    P13[\"13 DNS Config\u003cbr/\u003ePi-hole primary\u003cbr/\u003eGoogle fallback\"]:::gitops\n    DONE[\"Cluster\u003cbr/\u003eOperational\"]:::done\n\n    P1 --\u003e P2 --\u003e P3 --\u003e P4 --\u003e P5 --\u003e P6 --\u003e P7 --\u003e P8 --\u003e P9 --\u003e P10 --\u003e P11 --\u003e P12 --\u003e P13 --\u003e DONE\n\n    classDef infra fill:#ee0000,stroke:#cc0000,color:#fff\n    classDef k8s fill:#326ce5,stroke:#2457b5,color:#fff\n    classDef ha fill:#f5a623,stroke:#d4891a,color:#fff\n    classDef storage fill:#8bc34a,stroke:#689f38,color:#fff\n    classDef gitops fill:#ef7b4d,stroke:#d4642e,color:#fff\n    classDef done fill:#4caf50,stroke:#388e3c,color:#fff\n```\n\n\u003e **Legend:** \u003cspan style=\"color:#ee0000\"\u003eRed\u003c/span\u003e = Infrastructure · \u003cspan style=\"color:#326ce5\"\u003eBlue\u003c/span\u003e = K8s Bootstrap · \u003cspan style=\"color:#f5a623\"\u003eOrange\u003c/span\u003e = HA \u0026 Scale · \u003cspan style=\"color:#8bc34a\"\u003eGreen\u003c/span\u003e = Storage \u0026 DNS · \u003cspan style=\"color:#ef7b4d\"\u003eCoral\u003c/span\u003e = GitOps Handoff\n\n**Milestones:** After Phase 3 you have a working single-CP cluster. Phase 7 upgrades it to HA with kube-vip and 3 control planes. Phase 11 installs ArgoCD and hands off application management — from here, all app changes are GitOps-driven.\n\n### GitOps Application Delivery\n\nArgoCD uses the **App-of-Apps pattern** — one root application auto-discovers and deploys all others:\n\n```mermaid\nflowchart TB\n    DEV[\"fa:fa-user Developer\u003cbr/\u003egit push to main\"]:::git\n    GH[\"fa:fa-code-branch GitHub\u003cbr/\u003emain branch\"]:::git\n    ARGO[\"fa:fa-sync ArgoCD\u003cbr/\u003eDetects drift\"]:::argo\n    AOA[\"App-of-Apps\u003cbr/\u003eargocd/applications/*.yaml\"]:::argo\n\n    DEV --\u003e GH --\u003e ARGO --\u003e AOA\n\n    AOA --\u003e|\"13 apps\"| MEDIA[\"Media \u0026 Streaming\u003cbr/\u003ePlex · Jellyfin · Sonarr · Radarr\u003cbr/\u003eLidarr · Readarr · Bazarr · Prowlarr\u003cbr/\u003eSeerr · Tautulli · Jellystat\u003cbr/\u003ePinchflat · Random Streamer ✦\"]:::media\n    AOA --\u003e|\"3 apps\"| DL[\"Downloads\u003cbr/\u003eSABnzbd · Downloader+VPN\u003cbr/\u003eFlareSolverr\"]:::download\n    AOA --\u003e|\"4 apps\"| DASH[\"Dashboards \u0026 Monitoring\u003cbr/\u003eHomepage · Headlamp\u003cbr/\u003eGatus · Glances\"]:::dash\n    AOA --\u003e|\"9 apps\"| INFRA[\"Infrastructure \u0026 Security\u003cbr/\u003ePi-hole · Vaultwarden · Kopia Backup\u003cbr/\u003eKopia UI · Filebrowser · NFS Provisioner\u003cbr/\u003eNFS HDD Provisioner · MetalLB Config\u003cbr/\u003eDocker Registry\"]:::infra\n\n    MEDIA --\u003e HELM[\"helm/charts/*\u003cbr/\u003eLocal Helm charts\"]:::helm\n    DL --\u003e HELM\n    DASH --\u003e HELM\n    INFRA --\u003e HELM\n\n    HELM --\u003e DEPLOY[\"Deployed to Cluster\u003cbr/\u003eauto-sync · self-heal · prune\"]:::done\n\n    classDef git fill:#6e40c9,stroke:#6e40c9,color:#fff\n    classDef argo fill:#ef7b4d,stroke:#d4642e,color:#fff\n    classDef media fill:#e040fb,stroke:#c020d9,color:#fff\n    classDef download fill:#00bcd4,stroke:#0097a7,color:#fff\n    classDef dash fill:#f5a623,stroke:#d4891a,color:#fff\n    classDef infra fill:#607d8b,stroke:#455a64,color:#fff\n    classDef helm fill:#0f1689,stroke:#0a1060,color:#fff\n    classDef done fill:#4caf50,stroke:#388e3c,color:#fff\n```\n\n\u003e Every application manifest in `argocd/applications/` points to a local Helm chart in `helm/charts/`. ArgoCD renders the chart and applies it to the cluster. If someone manually changes a resource, ArgoCD **self-heals** it back to the Git-defined state.\n\n### Network \u0026 Traffic Flow\n\nAll services are exposed through a MetalLB + NGINX Ingress stack, with Pi-hole handling local DNS:\n\n```mermaid\nflowchart LR\n    subgraph CLIENT[\"Client\"]\n        USER[\"fa:fa-globe Browser / App\"]:::client\n    end\n\n    subgraph DNS[\"DNS Resolution\"]\n        PIHOLE[\"Pi-hole\u003cbr/\u003e192.168.1.242\"]:::dns\n    end\n\n    subgraph METALLB[\"MetalLB L2 ARP\"]\n        VIP240[\"Ingress VIP\u003cbr/\u003e.240\"]:::lb\n        VIP241[\"Plex VIP\u003cbr/\u003e.241\"]:::lb\n        VIP242[\"Pi-hole VIP\u003cbr/\u003e.242\"]:::lb\n        VIP245[\"Registry VIP\u003cbr/\u003e.245\"]:::lb\n    end\n\n    subgraph K8S[\"Kubernetes Cluster\"]\n        NGX[\"NGINX Ingress\u003cbr/\u003eHost-based routing\"]:::ingress\n        SVC[\"ClusterIP Services\"]:::svc\n        POD[\"Application Pods\"]:::pod\n        NGX --\u003e SVC --\u003e POD\n    end\n\n    USER --\u003e|\"*.homelab.local\"| PIHOLE\n    PIHOLE --\u003e|\"resolves to .240\"| VIP240\n    VIP240 --\u003e NGX\n\n    USER -.-\u003e|\"Direct IP .241 / .242 / .245\"| VIP241\n    VIP241 -.-\u003e|\"externalTrafficPolicy:\u003cbr/\u003eLocal\"| POD\n\n    classDef client fill:#6e40c9,stroke:#6e40c9,color:#fff\n    classDef dns fill:#4caf50,stroke:#388e3c,color:#fff\n    classDef lb fill:#f5a623,stroke:#d4891a,color:#fff\n    classDef ingress fill:#326ce5,stroke:#2457b5,color:#fff\n    classDef svc fill:#607d8b,stroke:#455a64,color:#fff\n    classDef pod fill:#e040fb,stroke:#c020d9,color:#fff\n```\n\n\u003e **Standard path:** Client queries Pi-hole → resolves `*.homelab.local` to `192.168.1.240` → MetalLB advertises via L2 ARP → NGINX Ingress routes by Host header → reaches the pod.\n\u003e\n\u003e **Direct path:** Plex (`.241`), Pi-hole (`.242`), and Docker Registry (`.245`) get dedicated MetalLB IPs, bypassing the ingress controller entirely.\n\n---\n\n### Node Inventory\n\n| Node | IP | Role | vCPU | RAM | Proxmox Host | GPU |\n|---|---|---|---|---|---|---|\n| k8s-cp | 192.168.1.200 | Control Plane | 2 | 3GB | .11 | — |\n| k8s-w1 | 192.168.1.201 | Worker | 3 | 4GB | .11 | — |\n| k8s-w2 | 192.168.1.202 | Worker | 4 | 6GB | .11 | Intel UHD 630 + NVIDIA GTX 1650 |\n| k8s-cp2 | 192.168.1.205 | Control Plane | 2 | 3GB | .8 | — |\n| k8s-cp3 | 192.168.1.206 | Control Plane | 2 | 3GB | .8 | — |\n| k8s-w4 | 192.168.1.204 | Worker | 4 | 6GB | .8 | — |\n\n**Totals:** .11 → 9c / 13GB (3 VMs) · .8 → 8c / 12GB (3 VMs)\n\n## Cluster Components\n\n| Component | Details |\n|---|---|\n| OS | Ubuntu 24.04 (cloud-init) |\n| Kubernetes | v1.32.x (kubeadm) |\n| Container Runtime | containerd 1.7.x |\n| CNI | Calico (tigera-operator) |\n| HA | kube-vip (ARP, leader election) — VIP `192.168.1.199` |\n| Load Balancer | MetalLB — pool `192.168.1.240–250` |\n| Ingress | NGINX Ingress Controller (`192.168.1.240`) |\n| Storage | Longhorn (replicated), local-path-provisioner |\n| GPU (k8s-w2) | Intel QuickSync (iGPU) + NVIDIA GTX 1650 (driver 535, device-plugin v0.14.5) |\n| GitOps | ArgoCD — App-of-Apps pattern, repo as source of truth |\n| Auto-update | Keel (poll-based image updates) |\n| Metrics | metrics-server |\n\n## Networking\n\n| Resource | IP | Purpose |\n|---|---|---|\n| Control Plane VIP | `192.168.1.199` | HA API server endpoint (kube-vip) |\n| Ingress LB | `192.168.1.240` | All `*.homelab.local` / `*.k8s.local` services |\n| Plex LB | `192.168.1.241` | Dedicated Plex LoadBalancer (`externalTrafficPolicy: Local`) |\n| Synology NAS | `192.168.1.28` | NFS media storage (16TB) |\n\n## Storage\n\n| Class | Provisioner | Use Case |\n|---|---|---|\n| `longhorn` (default) | Longhorn | Replicated PVCs — app config, databases |\n| `local-path` | Rancher local-path | Single-node fast local storage |\n\nNFS mounts on all workers:\n- `/mnt/nfs/nas-media` → `192.168.1.28:/data/nas-media` (Synology NAS, 16TB)\n- `/mnt/nfs/hdd-int` → `192.168.1.11:/data/hdd-internal` (Proxmox local HDD)\n\n## Services\n\n### Media Streaming\n\n| Service | URL | Node | Notes |\n|---|---|---|---|\n| Plex | `http://192.168.1.241:32400` / `plex.homelab.local` | k8s-w2 | GPU transcoding (Intel QuickSync + NVIDIA NVENC), dedicated LB IP |\n| Jellyfin | `jellyfin.homelab.local` | k8s-w2 | GPU transcoding, Intel QuickSync |\n| Tautulli | `tautulli.homelab.local` | k8s-w4 | Plex monitoring |\n| Jellystat | `jellystat.homelab.local` | k8s-w4 | Jellyfin monitoring (+ PostgreSQL DB) |\n| Pinchflat | `pinchflat.homelab.local` | k8s-w4 | YouTube channel archiver |\n| Random Streamer | `streamer.homelab.local` | k8s-w2 | Random video clips live stream (**my own development**) |\n\n### Media Automation (Arr Stack)\n\n| Service | URL | Purpose |\n|---|---|---|\n| Sonarr | `sonarr.homelab.local` | TV show management |\n| Radarr | `radarr.homelab.local` | Movie management |\n| Lidarr | `lidarr.homelab.local` | Music management |\n| Readarr | `readarr.homelab.local` | Book management |\n| Bazarr | `bazarr.homelab.local` | Subtitle management |\n| Prowlarr | `prowlarr.homelab.local` | Indexer management |\n| Seerr | `seerr.homelab.local` | Media request management |\n\n### Download Clients\n\n| Service | URL | Notes |\n|---|---|---|\n| SABnzbd | `sabnzbd.homelab.local` | Usenet downloader |\n| Downloader | `downloader.homelab.local` | Download client (via Gluetun VPN) |\n| FlareSolverr | — | Cloudflare bypass proxy for Prowlarr |\n\n### Cluster Management\n\n| Service | URL | Purpose |\n|---|---|---|\n| Homepage | `homepage.homelab.local` | Dashboard with service discovery |\n| Headlamp | `headlamp.k8s.local` | Kubernetes web UI |\n| ArgoCD | `argocd.homelab.local` | GitOps continuous delivery |\n| Keel | `keel.k8s.local` | Automated image updates |\n| Longhorn | `longhorn.k8s.local` | Storage dashboard |\n| Filebrowser | `filebrowser.homelab.local` | NFS file browser |\n\n### DNS Setup\n\nAdd to `/etc/hosts` (pointing to MetalLB ingress IP `192.168.1.240`):\n\n```\n192.168.1.240  homepage.homelab.local jellyfin.homelab.local sonarr.homelab.local radarr.homelab.local bazarr.homelab.local seerr.homelab.local tautulli.homelab.local sabnzbd.homelab.local readarr.homelab.local prowlarr.homelab.local downloader.homelab.local filebrowser.homelab.local jellystat.homelab.local lidarr.homelab.local plex.homelab.local\n192.168.1.240  argocd.homelab.local headlamp.k8s.local longhorn.k8s.local keel.k8s.local\n```\n\n## GPU Passthrough (k8s-w2)\n\nk8s-w2 has two GPUs passed through from Proxmox .11 via VFIO:\n\n| GPU | PCI ID | Use Case |\n|---|---|---|\n| Intel UHD 630 (iGPU) | `8086:3e9b` | Plex/Jellyfin QuickSync transcoding via `/dev/dri` |\n| NVIDIA GTX 1650 Mobile | `10de:1f91` | NVENC transcoding, CUDA workloads via `/dev/nvidia*` |\n\n- NVIDIA driver 535 loaded via systemd service (blacklisted from boot to avoid udev crashes)\n- `nvidia-container-toolkit` configured with containerd\n- `nvidia-device-plugin` v0.14.5 DaemonSet exposes `nvidia.com/gpu` resource\n- Plex container runs privileged with both `/dev/dri` and `/dev/nvidia*` mounted\n\n## Quick Start\n\nAll provisioning is done via Ansible from your local machine. See `ansible/setup.sh` for initial setup.\n\n```bash\ncd ansible\n\n# Run everything end-to-end (all 13 phases)\nansible-playbook playbooks/site.yml\n\n# Or run individual phases:\nansible-playbook playbooks/01-create-vms.yml          # Create VMs via cloud-init\nansible-playbook playbooks/02-prepare-nodes.yml        # Install containerd, kubeadm, kubelet\nansible-playbook playbooks/03-init-cluster.yml         # Bootstrap cluster + Calico + join workers\nansible-playbook playbooks/04-install-monitoring.yml   # Prometheus + Grafana\nansible-playbook playbooks/05-install-ingress.yml      # MetalLB + NGINX Ingress\nansible-playbook playbooks/06-add-remote-worker.yml    # Add 2nd Proxmox host nodes\nansible-playbook playbooks/07-convert-ha.yml           # kube-vip + 3 control planes\nansible-playbook playbooks/08-install-longhorn-deps.yml\nansible-playbook playbooks/09-setup-nfs-mounts.yml\nansible-playbook playbooks/10-add-hosts.yml            # Local DNS (/etc/hosts)\nansible-playbook playbooks/11-install-argocd.yml       # ArgoCD App-of-Apps\nansible-playbook playbooks/12-install-proxmox-glances.yml\nansible-playbook playbooks/13-set-dns.yml              # Pi-hole config\n```\n\nAccess the cluster:\n\n```bash\nexport KUBECONFIG=~/.kube/config-proxmox\nkubectl get nodes\n```\n\n## Teardown\n\n```bash\ncd ansible\nansible-playbook playbooks/teardown.yml\n```\n\n## Troubleshooting\n\n```bash\n# Check kubelet logs\nssh vineethvijay@192.168.1.200 \"sudo journalctl -u kubelet -f\"\n\n# Re-generate join token\nssh vineethvijay@192.168.1.200 \"sudo kubeadm token create --print-join-command\"\n\n# Reset a node\nansible-playbook ansible/playbooks/remove-node.yml -e \"target_node=k8s-w1\"\n\n# Check GPU on k8s-w2\nssh vineethvijay@192.168.1.202 \"nvidia-smi; ls /dev/dri/\"\n\n# Force-delete stuck pods\nkubectl delete pod \u003cname\u003e --force --grace-period=0\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvineethvijay%2Fprox-k8s-lab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvineethvijay%2Fprox-k8s-lab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvineethvijay%2Fprox-k8s-lab/lists"}