{"id":45990799,"url":"https://github.com/mlorentedev/kubelab","last_synced_at":"2026-06-30T00:00:52.631Z","repository":{"id":260867844,"uuid":"882537199","full_name":"mlorentedev/kubelab","owner":"mlorentedev","description":"5-node bare-metal cluster: Proxmox, K8s, Ansible, Tailscale. Plus Astro blog.","archived":false,"fork":false,"pushed_at":"2026-06-22T05:35:56.000Z","size":12676,"stargazers_count":0,"open_issues_count":380,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-06-22T06:22:28.615Z","etag":null,"topics":["astro","docker-compose","golang"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mlorentedev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","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":"2024-11-03T03:26:12.000Z","updated_at":"2026-06-22T04:45:28.000Z","dependencies_parsed_at":null,"dependency_job_id":"716d7255-960f-4b82-8c26-0dd11f7f105b","html_url":"https://github.com/mlorentedev/kubelab","commit_stats":null,"previous_names":["mlorentedev/astrowind","mlorentedev/cubelab","mlorentedev/kubelab","mlorentedev/mlorente.dev"],"tags_count":208,"template":false,"template_full_name":null,"purl":"pkg:github/mlorentedev/kubelab","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlorentedev%2Fkubelab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlorentedev%2Fkubelab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlorentedev%2Fkubelab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlorentedev%2Fkubelab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mlorentedev","download_url":"https://codeload.github.com/mlorentedev/kubelab/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlorentedev%2Fkubelab/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34947088,"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-06-29T02:00:05.398Z","response_time":58,"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":["astro","docker-compose","golang"],"created_at":"2026-02-28T20:17:10.478Z","updated_at":"2026-06-30T00:00:52.560Z","avatar_url":"https://github.com/mlorentedev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KubeLab\n\nPersonal Internal Developer Platform (IDP) — a hybrid-cloud infrastructure powering a portfolio of web services across homelab and cloud environments.\n\n## Architecture\n\n```\n                              Internet\n                                 │\n                        Cloudflare DNS (Terraform)\n                         *.kubelab.live, mlorente.dev\n                                 │\n                          ┌──────┴──────┐\n                          │  Hetzner VPS │  162.55.57.175\n                          │  (ARM, prod) │\n                          ├─────────────┤\n                          │ Traefik     │──→ api, web, errors,\n                          │ Headscale   │   grafana, gitea, n8n,\n                          │ (Docker     │   loki, portainer\n                          │  Compose)   │──→ Uptime Kuma (RPi3)\n                          └──────┬──────┘   via Tailscale proxy\n                                 │\n                    Headscale VPN mesh (WireGuard)\n                    ┌────────────┼────────────────────┐\n                    │            │                     │\n          ┌────────┴───┐  ┌─────┴──────┐    ┌────────┴────────┐\n          │   RPi4     │  │   ace1     │    │  Other nodes    │\n          │  Gateway   │  │  Staging   │    │                 │\n          ├────────────┤  ├────────────┤    │ ace2: GH Runner │\n          │ Pi-hole    │  │ K3s single │    │       + MinIO   │\n          │ CoreDNS    │  │ 13 pods:   │    │ Beelink: Ollama │\n          │ DHCP       │  │ api, web,  │    │ RPi3: Uptime    │\n          │            │  │ authelia,  │    │       Kuma      │\n          │ Split DNS: │  │ grafana,   │    │ Jetson: Pollex  │\n          │ *.staging  │  │ crowdsec,  │    │  (llama.cpp)    │\n          │ .kubelab   │  │ gitea, n8n │    │                 │\n          │ .live      │  │ loki, minio│    │                 │\n          └────────────┘  │ redis,     │    └─────────────────┘\n                          │ vector     │\n                          └────────────┘\n                   *.staging.kubelab.live\n                   staging.mlorente.dev\n```\n\n**Production:** Hetzner VPS running Docker Compose (migrating to K3s in Phase 2), public via `*.kubelab.live` and `mlorente.dev`.\n**Staging:** K3s single-node on ace1 (bare metal), accessible via Headscale VPN at `*.staging.kubelab.live` and `staging.mlorente.dev`.\n**Development:** Docker Compose on localhost with `*.kubelab.test` domains.\n\n### Key architectural decisions\n\n- **K3s over full K8s** — lightweight, single-binary, built-in Traefik and Helm controller\n- **Hybrid K8s packaging (ADR-021 Rev2)** — Kustomize for custom apps (simpler YAML), Helm official charts for third-party services\n- **SOPS for secrets** — age-encrypted YAML committed to Git, toolkit injects into K8s at deploy time\n- **Headscale self-hosted VPN** — replaces Tailscale SaaS, WireGuard-based mesh across all nodes\n- **Terraform for DNS** — Cloudflare zones managed declaratively, prod domains only\n- **Split DNS** — staging zones in `networking.staging_zones` (SSOT) → Headscale split DNS → RPi4 Pi-hole → CoreDNS\n\n## Tech Stack\n\n| Layer | Technology |\n|-------|-----------|\n| Orchestration | K3s v1.34, Kustomize, Helm (third-party) |\n| Reverse proxy | Traefik v3 (K3s HelmChartConfig) |\n| Auth | Authelia (SSO + OIDC) |\n| Security | CrowdSec (IDS + Traefik bouncer) |\n| Observability | Grafana + Loki + Vector |\n| Monitoring | Uptime Kuma (standalone on RPi3) |\n| DNS | Cloudflare (prod) + CoreDNS (staging) |\n| VPN | Headscale + Tailscale clients |\n| Secrets | SOPS (age encryption) |\n| CI/CD | GitHub Actions, multi-arch Docker builds |\n| IaC | Terraform, Ansible, Python toolkit |\n\n### Applications\n\n| App | Stack | Description |\n|-----|-------|------------|\n| [Web](apps/web/) | Astro + TypeScript + Tailwind | Portfolio and landing page |\n| [API](apps/api/) | Go + Gin | Backend services (newsletter, lead magnets) |\n| [Errors](edge/errors/) | HTML + Nginx | Custom Traefik error pages |\n\n### Self-hosted services\n\nGrafana, Loki, Authelia, CrowdSec, Gitea, MinIO, n8n, Redis.\n\n## Project Structure\n\n```\nkubelab/\n├── apps/                        Application source code\n│   ├── api/                     Go REST API\n│   └── web/                     Astro portfolio site\n│\n├── infra/\n│   ├── k8s/                     Kubernetes manifests\n│   │   ├── base/                Base manifests (staging defaults)\n│   │   └── overlays/            Kustomize overlays (staging, prod)\n│   ├── stacks/                  Docker Compose stacks (dev environment)\n│   ├── terraform/               Terraform DNS (Cloudflare)\n│   ├── ansible/                 Server provisioning playbooks\n│   └── config/\n│       ├── values/              Environment config (YAML per env)\n│       └── secrets/             SOPS-encrypted secrets\n│\n├── edge/                        Network edge (Traefik config, error pages)\n├── toolkit/                     Python CLI for platform management\n├── Makefile                     Development shortcuts\n└── CONTRIBUTING.md              Contributing guidelines\n```\n\n## Quick Start\n\n```bash\n# Prerequisites: Docker, Python 3.12+, Poetry, Make\n\n# Install toolkit\npoetry install\n\n# Initialize dev environment\nmake setup\n\n# Start development stack\nmake dev\n```\n\nServices available at `*.kubelab.test` (requires `/etc/hosts` entries — see `make setup-local-dns`).\n\n## Environments\n\n| Environment | Infrastructure | Access | Domains |\n|-------------|---------------|--------|---------|\n| Development | Docker Compose (local) | localhost | `*.kubelab.test` |\n| Staging | K3s on ace1 (bare metal) | Headscale VPN | `*.staging.kubelab.live`, `staging.mlorente.dev` |\n| Production | Docker Compose on VPS (→ K3s Phase 2) | Public internet | `*.kubelab.live`, `mlorente.dev` |\n\n## CI/CD Pipeline\n\nTrunk-based development (`feature/*` / `fix/*` → `master`, squash merge):\n\n- **Change detection** — only builds affected apps (api, web, errors)\n- **Multi-arch builds** — `linux/amd64` + `linux/arm64` Docker images\n- **Per-app SemVer** — `{app}-v{X.Y.Z}` tags via Release Please\n- **RC tags** — `{version}-rc.{N}` on PRs, auto-cleaned on merge\n\n## Toolkit CLI\n\nThe `toolkit` CLI manages the entire platform lifecycle:\n\n```bash\nalias tk='poetry run toolkit'\n\n# Deploy\nmake deploy-k8s ENV=staging              # Deploy K8s manifests (Kustomize)\nmake deploy TARGET=vps ENV=prod          # Deploy VPS services (Ansible)\nmake deploy TARGET=dns ENV=staging       # Deploy DNS gateway (Ansible)\nmake provision NODE=ace1 ENV=staging     # Provision a node (Ansible)\n\n# Secrets \u0026 config\ntk infra k8s apply-secrets --env staging # Inject SOPS secrets into K8s\ntk infra terraform plan                  # Plan DNS changes\ntk infra terraform apply                 # Apply DNS changes\n```\n\nSee [`toolkit/README.md`](toolkit/README.md) for full command reference.\n\n## Hardware Topology\n\n```\nHetzner VPS (ARM)      — Production: Docker Compose + Headscale\nAcemagic-1 (bare metal)— K3s staging (13 pods, all-in-one)\nAcemagic-2 (bare metal)— Platform node (GH Runner + MinIO)\nBeelink (bare metal)   — Ollama (LLM inference)\nRPi 4 (8GB)            — Network gateway: Pi-hole, CoreDNS, DHCP\nRPi 3 (1GB)            — External monitoring (Uptime Kuma)\nJetson Nano (4GB)      — Pollex (llama.cpp, GPU inference)\n```\n\nAll nodes connected via Headscale VPN mesh (WireGuard protocol, 8 nodes).\n\n## License\n\nSee [LICENSE](LICENSE) for details.\n\n## Author\n\nManuel Lorente — [mlorente.dev](https://mlorente.dev) | [GitHub](https://github.com/mlorentedev)\n\n## Documentation\n\nProject-bound knowledge lives in [`docs/`](docs/) (docs-as-code): ADRs, architecture, runbooks, troubleshooting, and lessons.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlorentedev%2Fkubelab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmlorentedev%2Fkubelab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlorentedev%2Fkubelab/lists"}