A home developer platform (Platform Engineering) on K3s. One GitHub organization, one dev environment, two nodes.
Nodes:
- Dell Optiplex 3060 Micro (i3-8100T, 16 GB RAM, 256 GB M.2 NVMe) - k3s server
- Dell Optiplex 5060 Micro (i5-8500T, 16 GB RAM, 256 GB M.2 NVMe) - k3s agent
The idea: a developer picks a template, a language and the options they need in Backstage, and the platform creates a repository, wires up CI/CD and security, builds a signed image and rolls it out through GitOps.
- In Backstage you choose a template, a language and options: the
as-skillharness, a security pipeline, nightly shutdown, deploy to dev. - A service repository is created with workflows from
ci-templates, plus a PR tok3swithapps/<name>/. - A push triggers CI on an ephemeral runner: tests, security, a build on golden images, push to Zot, signing.
deploy-bumpchangesimage.tagink3s/apps/<name>/values.yaml, Argo CD syncs, and Backstage shows the status.
- Orchestration: K3s, Traefik, cert-manager with an internal CA
- GitOps: Argo CD (app-of-apps, ApplicationSet), Sealed Secrets
- CI/CD: GitHub Actions, reusable actions, self-hosted runners on KEDA with scale-to-zero
- Supply chain: Zot registry, cosign signing, SBOM, rootless builds with buildah
- Hosts: Ansible
- Portal: Backstage
| Repository | Purpose | Status |
|---|---|---|
k3s |
Cluster GitOps repository: Argo CD, bootstrap, infra, apps | working |
homelab-infra |
Ansible roles for the hosts | working |
ci-templates |
Reusable CI actions | working |
backstage |
Developer portal | in development |
backstage-templates |
Repository templates and skeletons | in development |
golden-images |
Base images: cosign, SBOM, rootless buildah, hadolint | private |
helm-charts |
Shared Helm chart app: Deployment, Service, Ingress, ScaledObject with cron |
private, in development |
golden-images and helm-charts are private on purpose: their CI runs on the home runner and pushes to a private registry. For helm-charts, publishing the chart to Zot is still in progress.
- Home runners only for an allowlist of repositories. A pull request from a fork can add its own workflow and run someone else's code on the local network, so access is layered. The runner group serves only
backstageand the private build repositories, and runs from external contributors need manual approval. A NetworkPolicy lets runner pods reach only DNS, Zot through Traefik and the internet on ports 80 and 443; the rest of the LAN, the node and cluster services are closed, which a regression workflow checks. Runners are ephemeral, the PAT is removed from the job environment, and builds run without docker. - Traefik instead of ingress-nginx.
ingress-nginxwas retired in March 2026. StandardIngressresources are used rather thanIngressRoute, so the controller can be replaced. - An internal CA for 10 years. There is no domain, so Let's Encrypt does not fit; certificates are issued by cert-manager.
Two nodes, one dev environment, no staging or prod. Backstage and the templates are still in development, and nightly infrastructure shutdown is not implemented yet.
Deployment goes through Argo CD only.