ThisCalculator

Everyday

Homelab Kubernetes: Split Bootstrap from GitOps

Updated · Standard formula, shown below

Homelab Kubernetes: Split Bootstrap from GitOps

Stop rebuilding your homelab Kubernetes cluster by hand. The fix is to split the initial bootstrap (the raw cluster and its core components) from everything else, and manage the everything else with GitOps. You bootstrap once, commit the rest to Git, and let a controller reconcile the cluster to that declared state.

Why Hand-Rebuilding Is the Real Problem

Hand-running kubectl apply or helm install for every change works for a while, but it breaks down exactly when your cluster gets interesting. The failure mode is not a single bad command; it is the slow accumulation of drift. You install something, tweak a value, forget to save the manifest, and six months later the cluster is a snowflake that only you (and only vaguely) understand. Rebuilding from scratch becomes a dreaded weekend project because you have to remember every flag, every patch, and every secret you created imperatively.

The deeper issue is that a homelab is a pet, not a herd. You have one cluster, so you cannot afford to lose it. But treating it as a pet means you are afraid to break it, and that fear makes you avoid changes, which makes the drift worse. Splitting bootstrap from GitOps turns the cluster into something you can delete and restore from a clean Git checkout, which paradoxically makes you more willing to experiment.

What Exactly Is Bootstrap vs. GitOps?

Bootstrap is the minimal set of actions that turn bare metal (or a VM, or a cloud instance) into a Kubernetes cluster with a working control plane and a way to run workloads. This includes container runtime installation, kubeadm or k3s setup, CNI (network plugin), and often a storage driver. GitOps is the ongoing management of everything that runs on the cluster after that: namespaces, deployments, services, ingress rules, operators, and even the configuration of the cluster itself (via operators like Cluster API or Crossplane).

The split is clean: bootstrap is imperative and idempotent (you can run it again and get the same result), while GitOps is declarative and continuous. The bootstrap process should end with a single GitOps controller (like Argo CD or Flux) installed and pointed at your Git repository. From that moment, you never SSH into a node to change a manifest again.

How to Split Them in Practice

A practical split looks like this:

  1. Create a Git repository with a clear directory structure: cluster/ for core infrastructure (namespaces, operators, CRDs) and apps/ for your actual workloads.
  2. Write a bootstrap script (bash, Ansible, or a cloud-init) that installs the container runtime, initializes the cluster (kubeadm or k3s), installs the CNI, and then installs the GitOps controller using a static manifest from a known version.
  3. Point the GitOps controller at your repo using a single Application (Argo CD) or Kustomization (Flux) that references the cluster/ path.
  4. Commit all subsequent changes (new apps, config tweaks, upgrades) to Git. The controller applies them automatically.
  5. Test the restore by deleting the cluster and running the bootstrap script again. It should bring up a cluster that is functionally identical to the one you deleted, within the time it takes to pull images.

The key detail is that the bootstrap script itself is also versioned in Git, but it is not applied by GitOps. It is run once, by hand or by a CI job, and its only job is to get the controller running.

Choosing Your GitOps Controller

Two controllers dominate the homelab space: Argo CD and Flux. Both are mature, but they have different philosophies.

  • Flux is built around Kustomize and Helm, and it works directly with Git repositories, watching for changes and reconciling. It is slightly more minimal and integrates tightly with the Kubernetes ecosystem (e.g., image automation, HelmRelease CRDs).
  • Argo CD is more application-centric, with a web UI, sync policies, and a concept of “Applicationsâ€� that can point to Helm charts, Kustomize overlays, or plain YAML. It is heavier but more visual.

For a homelab, either works. A common choice is Flux if you want a lightweight, Git-native approach; Argo CD if you want a UI and are already familiar with it. The important thing is to pick one and commit to it, because the whole point is to stop doing things by hand.

Common Pitfalls and Misconceptions

The most common mistake is putting everything in GitOps, including the bootstrap itself. That sounds elegant but creates a chicken-and-egg problem: you need a cluster to run the controller that manages the cluster. The bootstrap must be separate, even if it is just a shell script that installs k3s and then applies a manifest for Flux.

Another pitfall is treating secrets casually. You should not commit plaintext secrets to Git. Use a sealed-secrets controller (like Sealed Secrets) or an external secrets operator (like External Secrets Operator with a Vault or cloud KMS) so that the Git repo contains only encrypted or referenced secrets. This is not optional for a homelab that might accidentally become public.

A third misconception is that GitOps means you can never SSH into a node again. That is true for cluster state, but not for node maintenance (kernel updates, hardware issues). The split is about what runs on the cluster, not about the OS underneath.

Costs and Benefits of the Split

The upfront cost is real. You have to learn a GitOps tool, restructure your manifests, and write a bootstrap script that is actually reproducible. For a small homelab, this can take a weekend. The benefit is that you can delete your cluster and rebuild it in under an hour, with zero manual steps beyond running one script. You also get a history of every change, which helps when something breaks after an upgrade.

There is also a cost in complexity. GitOps adds a moving part (the controller) that can fail, and you need to understand its sync behavior. But for most homelabs, the reliability gain outweighs the complexity, because the alternative is a cluster that is impossible to rebuild.

Alternatives to the Split

If the full GitOps split feels like overkill, there are lighter alternatives. You could use a single script that does everything (bootstrap and app deployment) and just rerun it when you need to rebuild. That works, but it is not continuous: changes are applied only when you run the script, not when you push to Git.

Another alternative is to use a Kubernetes distribution that includes GitOps out of the box, like k3s with a bundled Flux, or Talos Linux with its declarative configuration. These reduce the bootstrap to a single command, but they still require you to manage the cluster state declaratively.

The split is not the only way, but it is the one that gives you the best balance of reproducibility and continuous operation for a homelab.

Frequently Asked Questions

Q: Can I use the same Git repo for bootstrap and GitOps?
A: Yes, but keep them in separate directories. The bootstrap script can live in a bootstrap/ folder, and the GitOps controller should point to a cluster/ or apps/ path. The controller will ignore the bootstrap folder, so it is safe to keep everything in one repo.

Q: What if I already have a hand-built cluster? Can I migrate?
A: Yes. Start by exporting your current manifests (e.g., kubectl get all -o yaml), clean them up, and commit them to Git. Then install a GitOps controller and point it at the repo. The controller will adopt the existing resources, and you can gradually remove imperative commands from your workflow.

Q: Do I need to use Helm or Kustomize with GitOps?
A: No, but they help. GitOps controllers can apply plain YAML directly. Helm and Kustomize are useful for managing variations (e.g., dev vs. prod) and for packaging complex apps, but they are not required. Start with plain YAML if you are new.

Q: How do I handle secrets in Git?
A: Use a tool like Sealed Secrets (which encrypts secrets into a CRD) or External Secrets Operator (which fetches from a secret store). Never commit plaintext secrets. The bootstrap script can install the secret controller, and then all secrets in Git are encrypted.

Q: What happens if the GitOps controller fails or the repo is unreachable?
A: The cluster keeps running with the last applied state. The controller will retry and log errors. You can also configure a local cache or a mirror, but for a homelab, a simple retry loop is usually enough. The key is that you can always re-run the bootstrap script to get back to a known state.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.