The Problem
Deploying to Kubernetes without GitOps often means:- Imperative chaos:
kubectl applyfrom laptops, scripts that “usually work,” and no clear record of what’s actually running. - Drift: Someone makes a quick fix directly in the cluster. Now the running state doesn’t match any source of truth.
- No audit trail: “Who deployed that change?” requires digging through CI logs, Slack messages, and hoping someone remembers.
- Environment inconsistency: Staging and production diverge over time because deployments are manual and error-prone.
- Rollback friction: Something breaks in production. How do you get back to the previous state? Which commit was that?
How Kube Starter Kit Addresses This
I’ve implemented GitOps with ArgoCD, where Git is the single source of truth for your cluster state: Git as the source of truth: Everything running in the cluster is defined in Git. If it’s not in the repo, it shouldn’t be in the cluster. Continuous reconciliation: ArgoCD watches your Git repository and automatically syncs the cluster to match. Drift gets corrected automatically. App of Apps pattern: A hierarchical structure where one ArgoCD Application manages other Applications. Add a new service by adding a file; ArgoCD picks it up automatically. Environment separation: Each cluster (staging, production) has its own rendered manifests. Same source templates, different configurations. Audit everything: Every deployment is a Git commit. Who changed what, when, and why is all in the commit history.What’s Included
ArgoCD Structure
App of Apps Hierarchy
ArgoCD uses a three-tier Application structure:Project Organization
ArgoCD Projects provide logical separation:| Project | Purpose |
|---|---|
| argocd | ArgoCD’s own configuration and app-of-apps |
| infrastructure | Platform components (cert-manager, ingress, etc.) |
| services | Application workloads |
Deployment Flow
1
Make changes
Update Kubernetes manifests or Helm values in the
src/ directory.2
Render manifests locally
Run
mise run render-cluster <cluster> from the application directory to generate environment-specific manifests to rendered/staging/ or rendered/production/.3
Commit and push
CI validates that rendered manifests are up-to-date (no git diff after re-rendering).
4
ArgoCD detects change
ArgoCD notices the rendered manifests differ from cluster state.
5
Automatic sync
ArgoCD applies the changes to the cluster. With auto-sync enabled, this happens automatically.
6
Verification
ArgoCD reports sync status and health. Failed deployments are visible immediately.
Configuration Management
Applications are enabled/disabled via a simple values file:src/infrastructure/ and add an entry to the values file. ArgoCD picks it up on the next sync.
Repository Access
ArgoCD authenticates to your Git repository using SSH deploy keys. During bootstrap, a mise task generates a key pair and creates a Kubernetes Secret with the private key. You add the public key to your GitHub repository as a deploy key. No credentials in Git. ArgoCD discovers the repository secret automatically via theargocd.argoproj.io/secret-type=repository label. See cluster bootstrap for setup instructions.
Key Design Decisions
| Decision | Rationale |
|---|---|
| ArgoCD over Flux | Better UI for visibility, more mature ecosystem, easier onboarding for teams new to GitOps. |
| App of Apps over ApplicationSets | More explicit control over the hierarchy. ApplicationSets are powerful but can be harder to reason about. |
| Rendered manifests in Git | What’s deployed is exactly what’s in Git. No runtime templating surprises. Easier to audit and diff. |
| Bootstrap-generated deploy keys | Deploy keys are generated during bootstrap and stored as Kubernetes Secrets. Simple, no external dependencies. |
| Auto-sync enabled | Reduces manual toil. If you commit it, it deploys. Rollback is just reverting a commit. |