A Kubernetes platform operated through GitOps
An RKE2-based Kubernetes platform where cluster state lives in Git and Argo CD reconciles it, replacing hand-applied manifests with reviewed changes.
Problem
Workloads were deployed by running commands against servers or clusters directly. The real state of production was whatever had last been applied, and nobody could easily answer what changed, when, or why.
Constraints
- On-premises infrastructure; no managed Kubernetes service available.
- A small team had to operate it, so day-two operations needed to be boring.
- Hardened defaults were required from the start, not added later.
Decisions
- RKE2 as the Kubernetes distribution
- Security-focused defaults, CIS-aligned configuration and a straightforward upgrade path suited an on-premises, security-conscious environment.
- Argo CD with an app-of-apps layout
- Every cluster add-on and workload is declared in Git, so drift is visible and a cluster can be rebuilt from the repository.
- Separate repositories for platform and application configuration
- Platform changes and application releases have different reviewers, cadences and blast radius.
Architecture
Git repositories
Platform add-ons and application manifests stored as Helm values and Kustomize overlays.
- GitLab
- Helm
- Kustomize
Reconciliation
Argo CD watches the repositories and syncs desired state; drift is reported and corrected.
- Argo CD
Cluster
RKE2 control plane and worker nodes with network policies and pod security standards enforced.
- RKE2
- Calico or Canal
Ingress and secrets
Ingress controller with managed certificates; secrets injected from an external store, never committed.
- Ingress NGINX
- cert-manager
- Vault
Observability
Metrics, logs and alerts for both the platform and the workloads running on it.
- Prometheus
- Grafana
- Loki
Build notes
Implementation
The cluster was built with infrastructure as code and RKE2’s configuration files, so node provisioning and cluster bootstrap could be repeated. Argo CD was the first workload installed; from that point on it installed everything else.
The app-of-apps pattern gave a single root application per cluster that declared the platform layers in order: networking and policy, certificates, ingress, secrets integration, observability, then team workloads.
Challenges
Ordering mattered. Some add-ons depend on custom resource definitions that others install. Sync waves made the dependency order explicit instead of relying on retries.
Secrets were the main design question. Keeping them out of Git entirely, and injecting them at runtime from an external store, kept the repositories safe to share with the wider team.
Outcome
Changes to the platform now go through merge requests with a reviewable diff and an audit trail. Rolling back means reverting a commit, and rebuilding a cluster is a documented, repeatable procedure rather than tribal knowledge.
What I learned
GitOps only pays off when everything is in Git. One manually applied exception erodes trust in the whole model.