k8s-gitops: a Flux-managed home cluster whose real home is a self-hosted Forgejo
GitOps principles to define kubernetes cluster state via code
At a glance
- What is it?
- billimek/k8s-gitops defines a Kubernetes cluster as YAML in a git repository, reconciled by Flux, with workloads split by namespace and upgrade automation handled by Renovate and tuppr. The interesting decision is not the tooling but where the source of truth lives, on a self-hosted Forgejo instance rather than on GitHub.
- Who is it for?
- k8s-gitops suits an operator who wants a home cluster to be reproducible from a repository, with the platform components declared alongside the workloads, and who is comfortable running Forgejo to host that repository. It does not suit a team that needs a hosted Git provider, a managed cluster, or an opinionated supported distribution, because the repository is one person's configuration rather than a product.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly YAML, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The git repository is the cluster, and Forgejo is where it actually lives
The project states its purpose in the title: a GitOps workflow for a Kubernetes cluster, defining cluster state via code. The mechanism is flux, with the README pointing at the flux2 repository, and the code that defines the state resides in the repository itself.
The detail that shapes everything else is where that repository is hosted. The primary hosting is a self-hosted Forgejo instance, and the GitHub repository is a read-only mirror. The project URLs point at the Forgejo host rather than GitHub, and there is a .forgejo/ directory in the tree, while a .claude/ directory and AGENTS.md and CLAUDE.md files sit at the root alongside the .justfile.
That arrangement is unusual and worth thinking about before adopting anything here. A read-only GitHub mirror means the pull request workflow, the issue tracker and the code review happen on the author's own Forgejo server, not on GitHub. The Renovate configuration is present in the tree as a renovate.json5 file with a .renovate/ directory, and the automation is set up to open pull requests against the Forgejo instance. If you fork this, the pull requests go to a Git server you are running, and the accessibility of the original depends on the author's instance staying up.
The project is part of what the README calls the k8s@home community, with a Discord server linked for it, and the topic tags include k8s-at-home alongside flux, gitops, helm and kubernetes. The audience is a home lab operator, and the primary language of the repository is recorded as YAML, which is an accurate description of a cluster defined by manifests.
Workloads are organised by namespace, not by application
The workload layout is a directory per namespace under kubernetes/, and the list is short enough to read in one pass: cert-manager, default, flux-system, kube-system, monitoring, rook-ceph and system-upgrade.
That is a platform-oriented taxonomy rather than an application-oriented one, and it is the right choice for a cluster where the platform components and the workloads have different lifecycles. The namespace named flux-system is where the GitOps controllers live, which is the conventional home for them. cert-manager is there because certificate issuance needs to be a platform concern once more than one workload needs TLS. rook-ceph is a storage operator, so the cluster's persistent volumes are managed by the same reconciliation loop as everything else. system-upgrade is a namespace of its own, which is a strong hint about how operating system upgrades are handled in this cluster. monitoring sits alongside them.
The default namespace is listed as its own workload directory, which is worth noting because it means whatever runs in the default namespace is also managed here rather than being left outside the GitOps boundary.
The setup documentation is separated from the workload definitions on purpose. The README points at setup/README.md for setup and bootstrapping a new cluster, and at setup/talos/README.md for hardware and infrastructure detail, with the latter explicitly described as covering more detail about hardware and infrastructure. The topics include helm, and the Renovate entry in the README mentions a new helm chart as one of the things it opens pull requests for, so charts are part of the picture even though the README does not spell out a charts directory.
The separation between a setup directory and a kubernetes directory is a useful convention. One directory describes how you get a cluster, the other describes what runs on it, and they change for different reasons.
Renovate and tuppr split the upgrade problem in two
The automation section is short and names two projects, each taking one half of the upgrade problem.
Renovate is described as keeping workloads up to date by scanning the repository and opening pull requests when it detects a new container image update or a new helm chart. That is the half of the problem inside the workloads, the images and the charts you deploy, and the mechanism is a pull request rather than an automatic apply, which means a human or an automation of your choosing decides whether the change lands.
tuppr takes the other half. It automatically upgrades talos and kubernetes to new versions as they are released. The Talos part matters, because Talos is the operating system of the cluster in this design, and an OS upgrade is not the same kind of operation as bumping a container image. The README notes that the setup documentation covers the Talos cluster setup in more detail about hardware and infrastructure, and the topics list k8s-at-home, which is where this kind of home setup is discussed.
So the division of labour is clean. Workload versions move through review in the repository, and the platform version moves on its own schedule. The system-upgrade namespace in kubernetes/ is presumably where the cluster-side automation is declared, though the README does not describe its contents.
The alternative to this design is the obvious one: run a distribution's own package manager and hope the versions line up. The trade-off in exchange is that you now own the cluster's upgrade path, and the platform upgrade happens without a pull request, which means the safety net is the Talos rollback rather than a reverted commit.
Where to start when you clone the repository
There is no install command to run, and the project says so by pointing you at two documents instead of a sequence of steps.
The first is the setup directory, linked as setup/README.md, which the README says has more detail on setup and bootstrapping a new cluster. The second is the Talos cluster setup, linked as setup/talos/README.md, which has more detail about hardware and infrastructure. The two are separate because they answer separate questions: one is what to run, the other is what to run it on.
So the practical first-use path is: read the hardware and infrastructure notes to understand the machines the cluster expects, then read the setup document for the bootstrap procedure, then point flux at your own repository and let the workload directories be reconciled. The workload directories themselves need no editing to come up, since the namespaces listed in the README are already declared, and the automation is already pointed at the repository.
The task runner is the .justfile in the root, with a .justfiles/ directory beside it. The README does not list the individual recipes, so the commands are there to be read rather than documented, and the same goes for the Renovate configuration in renovate.json5 and the .renovate/ directory.
If you are copying this design rather than adopting it wholesale, the pieces are separable. The workload layout, the Renovate setup and the flux-system configuration are the parts that generalise. The Talos-specific bootstrap and the tuppr upgrade path are the parts tied to this particular cluster.
A Just based workflow and agent instructions live next to the manifests
The repository root has a .justfile and a .justfiles/ directory, which is the task runner entry point, and it fits the pattern of a repository where a developer and an operator are the same person. The README does not document the individual recipes, so the commands in it are discoverable by reading the file rather than by reading the documentation, which is a real limitation for anyone adopting it as a starting point.
The other thing at the root that stands out is the presence of agent instructions. There is an AGENTS.md and a CLAUDE.md at the top level, plus a .claude/ directory. For a project whose entire content is YAML manifests, that is a meaningful signal about how the repository is maintained, since it suggests the manifests are edited with tool assistance rather than by hand.
The .gitattributes, .gitignore, LICENSE and renovate.json5 complete the root, with the latter sitting at the top level as well as the .renovate/ directory, which is where the more detailed Renovate configuration lives.
What is notably absent from the root is any kind of README content about day to day use. The README is a set of links: to the setup documentation, to the workload directories, to the two automation projects, to the community. Everything else is behind those links. For a project of this kind that is a defensible choice, since the setup document is where a new operator would need to start, but it means there is no single place to see the whole design.
Where k8s-gitops is the wrong tool
The first wrong-tool case is a team that wants a supported distribution rather than one operator's configuration. This is a home lab repository. The setup documentation describes bootstrapping a cluster on Talos hardware, the workload set is a small personal platform, and the community is a Discord for a k8s@home group. There is no support commitment, no compatibility matrix and no release cadence, and the repository has no GitHub releases at all.
The second case is anyone whose Git provider is GitHub and who does not want to run a Forgejo instance. The mirror here is read-only, so the pull request flow that Renovate depends on does not work against GitHub. An operator who wants this design with GitHub as the source of truth has to host their own Forgejo or adapt the automation.
The third case is a non-Talos cluster. The upgrade automation is described in terms of Talos and Kubernetes versions, and the setup directory is organized around a Talos cluster. Flux itself is platform independent, so the reconciliation model transfers, but the system-upgrade namespace and the tuppr workflow are specific to this setup and would not apply to a cluster built on a managed control plane or a conventional distribution.
The fourth case is a production environment with a change control process. Renovate opening pull requests is a good fit for review, and tuppr upgrading the platform without one is not. The second half of that automation assumes you are comfortable with the cluster changing underneath you, which is fine at home and wrong in a shared environment.
On the practical side, the last push was on 2026-10-20 and the repository is not archived, so the work is current. The licence is Apache-2.0 with a LICENSE file at the root, which is permissive and lets you reuse the manifests and the setup scripts in a commercial setting without a copyleft obligation.
Editorial conclusion
k8s-gitops suits an operator who wants a home cluster to be reproducible from a repository, with the platform components declared alongside the workloads, and who is comfortable running Forgejo to host that repository. It does not suit a team that needs a hosted Git provider, a managed cluster, or an opinionated supported distribution, because the repository is one person's configuration rather than a product. Before adopting any part of it, read the setup documentation in setup/README.md and the Talos hardware notes in setup/talos/README.md, clone from the Forgejo instance rather than the GitHub mirror, and check that your cluster is Talos based before assuming the system-upgrade namespace and tuppr workflow apply to your platform.
Frequently asked questions
What is billimek/k8s-gitops?
It is a GitOps workflow for a Kubernetes cluster, using flux to automate cluster state from code that resides in the repository. The project defines cluster and workload state as YAML, with workloads organised by namespace under kubernetes/.
Where is the k8s-gitops repository hosted?
The primary hosting is a self-hosted Forgejo instance, and the GitHub repository is a read-only mirror. The project URLs point at the Forgejo host, and the repository carries a .forgejo/ directory, so the pull request flow that Renovate uses runs against that instance.
How does k8s-gitops handle upgrades?
Renovate scans the repository and opens pull requests when it detects a new container image or a new helm chart, while tuppr automatically upgrades talos and kubernetes to new versions as they are released. The workload versions therefore move through review and the platform version moves on its own schedule.
What workloads does the k8s-gitops cluster run?
The workloads are organised by namespace under kubernetes/, with directories for cert-manager, default, flux-system, kube-system, monitoring, rook-ceph and system-upgrade. Setup and bootstrapping are documented separately in setup/README.md, with hardware and infrastructure detail in setup/talos/README.md.
What license is k8s-gitops released under?
The project is licensed under Apache-2.0, with a LICENSE file at the root of the repository. The last push was on 2026-10-20 and the repository has no GitHub releases.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/billimek-k8s-gitops)Was this useful?