{"id":50299606,"url":"https://github.com/kogeler/k8s-lab","last_synced_at":"2026-05-28T11:32:21.569Z","repository":{"id":357097080,"uuid":"1235294117","full_name":"kogeler/k8s-lab","owner":"kogeler","description":"Reusable building blocks for a single-host Kubernetes laboratory on Debian-family Linux: LXC/LXD nodes driven by Cluster API (CAPN), Ansible roles, a Terraform module, and Helm charts (Calico, MetalLB L2 IPv6, kubeadm), with a Molecule + Vagrant + libvirt local test harness.","archived":false,"fork":false,"pushed_at":"2026-05-11T09:01:20.000Z","size":1155,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T11:07:33.965Z","etag":null,"topics":["ansible","calico","capi","capn","cluster-api","debian","helm","homelab","incus","k3s","kubeadm","kubernetes","kubernetes-lab","libvirt","lxc","lxd","metallb","molecule","terraform","vagrant"],"latest_commit_sha":null,"homepage":"https://kogeler.github.io/k8s-lab/","language":"HCL","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/kogeler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":"SECURITY.md","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-05-11T07:29:36.000Z","updated_at":"2026-05-11T10:20:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kogeler/k8s-lab","commit_stats":null,"previous_names":["kogeler/k8s-lab"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/kogeler/k8s-lab","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogeler%2Fk8s-lab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogeler%2Fk8s-lab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogeler%2Fk8s-lab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogeler%2Fk8s-lab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kogeler","download_url":"https://codeload.github.com/kogeler/k8s-lab/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogeler%2Fk8s-lab/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33607334,"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-28T02:00:06.440Z","response_time":99,"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","calico","capi","capn","cluster-api","debian","helm","homelab","incus","k3s","kubeadm","kubernetes","kubernetes-lab","libvirt","lxc","lxd","metallb","molecule","terraform","vagrant"],"created_at":"2026-05-28T11:32:20.646Z","updated_at":"2026-05-28T11:32:21.564Z","avatar_url":"https://github.com/kogeler.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# k8s-lab — Cluster API + LXC/LXD Kubernetes laboratory on Debian or Ubuntu Linux\n\n[![License: MIT](https://img.shields.io/github/license/kogeler/k8s-lab)](LICENSE)\n[![Latest release](https://img.shields.io/github/v/release/kogeler/k8s-lab?display_name=tag\u0026sort=semver)](https://github.com/kogeler/k8s-lab/releases)\n[![Last commit](https://img.shields.io/github/last-commit/kogeler/k8s-lab/main)](https://github.com/kogeler/k8s-lab/commits/main)\n[![Docs](https://img.shields.io/badge/docs-k8s--lab.romancello.net-2088FF?logo=readthedocs\u0026logoColor=white)](https://k8s-lab.romancello.net/)\n[![Kubernetes](https://img.shields.io/badge/kubernetes-v1.35-blue?logo=kubernetes\u0026logoColor=white)](doc/03-stack.md)\n[![Cluster API](https://img.shields.io/badge/cluster--api-v1.12-326CE5)](doc/03-stack.md)\n[![CAPN](https://img.shields.io/badge/CAPN-v0.8-228B22)](doc/03-stack.md)\n[![OS](https://img.shields.io/badge/OS-Debian%20%7C%20Ubuntu-A81D33?logo=debian\u0026logoColor=white)](doc/05-prerequisites.md)\n\n\u003e **Production-grade Kubernetes platform on a single bare-metal/VM box** —\n\u003e the same Cluster API pattern platform-engineering teams run in real\n\u003e environments, made almost as turnkey as `minikube` to spin up.\n\u003e\n\u003e Put LXD on the host. Run the bootstrap. You get a **self-hosted Cluster\n\u003e API management cluster** that provisions any number of workload clusters\n\u003e from a handful of Cluster API CRDs (`Cluster`, `ClusterClass`,\n\u003e Kubeadm/KCP templates, CAPN infrastructure CRs) flexibly describing the\n\u003e topology — Kubernetes version, control-plane size, worker pools,\n\u003e networking. Real kubeadm, real CNI, real load balancer. Nodes are unprivileged LXC system containers:\n\u003e **near-zero virtualisation overhead, seconds-not-minutes provisioning,\n\u003e strong host isolation** — no VM tax.\n\u003e\n\u003e - **Production patterns, lab footprint.** Real CAPI / CABPK / KCP / CAPN\n\u003e   management plane — not a `kind` / `minikube` / single-node k3s toy.\n\u003e - **Declarative all the way down.** Every Kubernetes object ships through\n\u003e   Helm via Terraform; cluster lifecycle is CRD-driven.\n\u003e - **Locally reproducible.** A Vagrant + libvirt harness runs the entire\n\u003e   canonical flow on a developer laptop — same code path as a real host.\n\u003e - **Test any change at any layer.** Molecule scenarios cover every role\n\u003e   (substrate → bootstrap → pivot → workload → add-ons); chart-side\n\u003e   `helm.sh/hook: test` Pods (external-L2 reachability + CNI viability)\n\u003e   then gate `terraform apply` — a broken data plane fails the deploy\n\u003e   instead of silently shipping a half-working cluster.\n\n---\n\n## What is k8s-lab?\n\n`k8s-lab` is a code repository for building a Kubernetes laboratory on a\n**single bare-metal Debian or Ubuntu Linux host** where the Kubernetes nodes are\nunprivileged LXC/LXD system containers, not virtual machines. The cluster\nlifecycle is managed end-to-end by Cluster API: a transient single-node `k3s`\nbootstrap cluster runs `clusterctl init`, then **pivots** management\nresponsibility onto a self-hosted management cluster, which then provisions\nthe actual workload cluster. After the pivot, the bootstrap LXC is destroyed.\n\nThe project ships **only reusable building blocks** — Ansible roles, a\nTerraform module, Helm charts, and a local test harness. Concrete environment\ncomposition (inventories, secrets, environment-specific tfvars, site-specific\nroot modules) lives in **separate private consumer repositories** that import\nthis code. The boundary is a deliberate architectural rule, not a missing\nfeature.\n\n## Who is this for?\n\n- **Homelab operators** with a single capable bare-metal host who want a real\n  multi-CP / multi-worker Kubernetes cluster with a real CNI, a real load\n  balancer, and a real CAPI control plane — without paying the VM tax.\n- **Cluster API contributors and CAPN users** looking for a reference\n  end-to-end pipeline for the LXC/LXD infrastructure provider.\n- **Platform engineers** evaluating LXC/LXD as a substrate for ephemeral\n  Kubernetes labs or per-developer environments.\n- **Educators** running a reproducible single-host CAPI demo.\n\nIt is **not** a managed-Kubernetes alternative, a multi-host installer, a\n`kind`/`minikube` replacement, or a turnkey \"one-command-deploy-to-production\"\ntool.\n\n## What ships in this repo\n\n- **14 Ansible roles** — host bootstrap, LXD substrate, bootstrap management\n  cluster, CAPI/CAPN install, pivot, validation gates, local harness.\n- **1 Terraform module** (`terraform/modules/workload_cluster`) — Cluster API\n  objects, machine templates, guest networking, cluster add-ons (CNI, MetalLB)\n  via the `hashicorp/helm` provider.\n- **5 Helm charts** (`charts/`) — `capi-cluster-class`, `capi-workload-cluster`,\n  `cni-calico`, `metallb`, `metallb-config`. Every Kubernetes object the\n  project creates is delivered through one of these — **no raw manifests, no\n  `kubectl apply -f`**.\n- **A Molecule + Vagrant + libvirt test harness** that runs the same canonical\n  flow on a developer laptop as on the production host.\n- **A `Makefile`** that ties the local lifecycle together — `make lint`,\n  `make test-local-e2e`, `make deploy-workload`, `make reset-all`,\n  `make clean-local`.\n\n## How does the bootstrap → pivot flow work?\n\n```mermaid\nflowchart LR\n    subgraph host[\"Debian or Ubuntu Linux host\"]\n        direction TB\n        snap[\"LXD snap (6/stable)\"]\n        bridge[\"br-ext6 Linux bridge\u003cbr/\u003e(external IPv6 ingress)\"]\n    end\n\n    host --\u003e|hosts LXC project\u003cbr/\u003ecapi-lab| boot\n\n    subgraph boot[\"1. Bootstrap (transient)\"]\n        direction TB\n        k3s[\"capi-bootstrap-0\u003cbr/\u003ek3s single-node LXC\"]\n        k3s --\u003e|clusterctl init| capi[\"CAPI + CAPN\u003cbr/\u003econtrollers\"]\n    end\n\n    boot --\u003e|helm install\u003cbr/\u003emgmt-1 Cluster CR| mgmt\n\n    subgraph mgmt[\"2. Management (self-hosted)\"]\n        direction TB\n        mgmtnodes[\"mgmt-1 LXC nodes\u003cbr/\u003e1 CP + 2 W\"]\n        capi2[\"CAPI + CAPN\u003cbr/\u003eself-hosted\"]\n        mgmtnodes -.-\u003e|clusterctl move| capi2\n    end\n\n    boot -.-\u003e|destroyed by\u003cbr/\u003ecleanup_bootstrap| trash[(\"/dev/null\")]\n\n    mgmt --\u003e|terraform apply\u003cbr/\u003eworkload_cluster| workload\n\n    subgraph workload[\"3. Workload\"]\n        direction TB\n        wnodes[\"lab-default LXC nodes\u003cbr/\u003e3 CP + 2 W\"]\n        wnodes --\u003e calico[\"Calico CNI\u003cbr/\u003edual-stack v4/v6\"]\n        wnodes --\u003e metallb[\"MetalLB L2\u003cbr/\u003eIPv6 VIPs on eth1\"]\n    end\n```\n\nSee [`doc/02-architecture.md`](doc/02-architecture.md) for the canonical\nnine-step flow, the dual-NIC node design, and the Ansible / Terraform / Helm\nownership split.\n\n## Documentation\n\nThe full user-facing documentation is rendered as a MkDocs site at\n**\u003chttps://k8s-lab.romancello.net/\u003e**. The same content lives under\n[`doc/`](doc/) for offline reading on GitHub. Common entry points:\n\n- [Overview](doc/01-overview.md) — core idea, goals, non-goals.\n- [Architecture](doc/02-architecture.md) — bootstrap and pivot flow, dual-NIC\n  model, layer ownership, validation gates.\n- [Stack](doc/03-stack.md) — every external dependency this repo pins.\n- [Quickstart (local)](doc/06-quickstart-local.md) — Vagrant + libvirt local\n  end-to-end workflow.\n- [Deployment guide](doc/07-deployment-guide.md) — real-host deployment through\n  a private consumer repository.\n- [Configuration reference](doc/08-configuration-reference.md) — project\n  globals, role inputs, Terraform inputs and outputs, chart values.\n\nThe architectural source of truth lives under [`plans/`](plans/) — continuous\n`§N` numbering across all plan files. The `doc/` chapters summarise and\noperationalise those plans for end users.\n\n## Layout\n\n```\nansible/       # 14 reusable roles (host, LXD substrate, bootstrap, pivot, harness)\ncharts/        # 5 local wrapper Helm charts (CAPI CRs + cluster add-ons)\nclusterctl/    # Reserved; runtime clusterctl.yaml is rendered by roles\ndoc/           # User-facing documentation (chapters 01..14)\nplans/         # Architectural plan files (source of truth)\nscripts/       # Local automation helpers\nterraform/     # workload_cluster module\ntests/         # Molecule scenarios + Vagrant harness + Terraform fixtures\nLICENSE        # MIT\n.artifacts/    # Runtime-only: kubeconfigs, tfvars handoff, ephemeral trust\n```\n\n## Local workflows\n\nAll entry points are local-only by design. Real-environment composition\n(`make deploy TARGET=…`) lives in private consumer repositories.\n\n```bash\nmake lint                 # static checks: yamllint, ansible-lint, terraform fmt, helm lint\nmake test-local-harness   # bring up Vagrant VM, verify harness prerequisites\nmake test-local-e2e       # full local pipeline (plan §13.2)\nmake deploy-workload      # terraform apply workload cluster on existing mgmt\nmake reset-all            # full reverse destroy chain (plan §19.2)\nmake clean-local          # tear down local harness state fast\n```\n\n## Conventions\n\n- **Two-NIC node design** — `eth0` = internal dual-stack (default route,\n  kubelet node IP, egress); `eth1` = external IPv6-only (ingress, NodePort,\n  MetalLB VIP).\n- **Unprivileged LXC only** for Kubernetes nodes (plan `§2.8`). Privileged\n  LXC is closed by design.\n- **Ansible owns** host / bootstrap / harness; **Terraform owns** Cluster API\n  objects, guest networking, kube-proxy policy, cluster add-ons (plan `§2.7`).\n- **Helm-first delivery** — every Kubernetes object goes through a chart in\n  `charts/`. Raw manifests / `kubectl apply -f` are forbidden (plan `§2.9`).\n- **Native-first Ansible** — `shell` / `command` / `script` only as a\n  documented last-resort fallback (plan `§2.6.1`).\n- **Binaries under `/opt/capi-lab`** — no custom APT repositories (plan\n  `§2.2`).\n\n## Status\n\nStage 1 is closed as **v1.0**. The Stage 2 backlog\n([`plans/PLAN-stage2-common.md`](plans/PLAN-stage2-common.md)) lists opt-in\nitems that may be built on top of the working substrate without regressing\nit.\n\n## License\n\n`k8s-lab` is licensed under the [MIT License](LICENSE). The license applies\nto this repository's code and documentation. Third-party tools, providers,\ncharts, collections, and container images keep their own licenses.\n\n## Citation\n\nA machine-readable citation file is provided at [`CITATION.cff`](CITATION.cff).\nGitHub renders a \"Cite this repository\" widget on the repo sidebar from it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkogeler%2Fk8s-lab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkogeler%2Fk8s-lab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkogeler%2Fk8s-lab/lists"}