A pragmatic GitOps baseline for small platform teams
What I set up (and agree on with the team) before anyone installs Argo CD. Spoiler: most of it is conventions, not tools.
I’ve helped a fair number of teams move to GitOps by now, and I’ve noticed something a bit uncomfortable: when it goes wrong, it’s almost never Argo CD’s fault.
It goes wrong because nobody sat down and agreed on two boring things: where stuff lives and who’s allowed to change it. So before I install anything, I try to get the team to agree on a baseline that fits on a single page. Here’s roughly what goes on that page.
1. Split repositories by how often they change
This is the layout I keep coming back to for teams of about 3 to 15 engineers:
| Repository | What it owns | How often it changes |
|---|---|---|
infra-live |
Terraform for accounts, networks, clusters | Weekly-ish |
platform-addons |
Cluster add-ons: ingress, cert-manager, Kyverno | Weekly-ish |
apps-deploy |
Helm values / Kustomize overlays per environment | Many times a day |
| app repos | Source code, Dockerfile, chart | Many times a day |
People usually push back on pulling deployment config out of the app repo. “Isn’t that just one more repo to deal with?” Fair question. What sells it is this: promoting from staging to production turns into a small pull request that someone actually reads. After a few weeks nobody wants to go back.
2. One “app of apps” per cluster
I like an ApplicationSet that just looks at folders:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: platform-addons
spec:
generators:
- git:
repoURL: https://github.com/acme/platform-addons
revision: main
directories:
- path: addons/*
template:
metadata:
name: '{{path.basename}}'
spec:
project: platform
source:
repoURL: https://github.com/acme/platform-addons
targetRevision: main
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
syncPolicy:
automated: { prune: true, selfHeal: true }
Want a new add-on? Add a folder. Want it gone? Delete the folder. Nobody has to click around in the UI, and nobody has to remember what they clicked.
3. Guardrails first, speed second
A few rules I push for from day one:
- Policy as code with Kyverno. Start in
Auditmode so you don’t break anything, read the reports for a couple of weeks, then flip toEnforce. - Terraform plans on every PR, and applies only from CI using short-lived credentials (OIDC). No static keys sitting in a secrets store forever.
- Treat drift as a bug. At some point
selfHealwill undo somebody’skubectl editand they’ll be annoyed. Don’t turn it off. That’s your chance to show them the workflow.
4. Teach the workflow, not just the tools
The part I enjoy most is the last one. I run a hands-on session where every engineer ships one change end to end: open the PR, read the plan, merge, watch Argo CD sync, then roll it back.
It sounds basic, but something clicks once people have done it themselves. The platform stops feeling like a black box that only “the DevOps person” understands, and they start using it for real.
If you’re planning a GitOps move, or you do it completely differently and think I’m wrong, I’d love to hear about it.
Written by Daniel Ulisses
Cloud Architect & DevOps Engineer. I write about cloud platforms, GitOps and bringing AI into DevOps workflows. Questions or feedback on this post? I'd love to hear it.