Skip to content

Blog

GitOps as a Product: Building Self‑Service with ArgoCD

Banner image Banner image

GitOps as a Product: Building Self‑Service with ArgoCD

Every platform team reaches the same inflection point. The Kubernetes clusters are stable, GitOps is working, ArgoCD is syncing everything cleanly — and then a developer submits a ticket asking the platform team to onboard their new service. Then another. Then a queue forms.

You've built good infrastructure. But it's not a product yet. It's a service that requires the platform team to be in the loop for every new workload. That doesn't scale, and it creates exactly the kind of toil that drains platform engineers and slows down everyone else.

The fix isn't to work faster. It's to make onboarding something developers can do themselves — within guardrails the platform team controls.

How I Standardised Kubernetes Deployments with ArgoCD

Banner image Banner image

How I Standardised Kubernetes Deployments with ArgoCD


Why this exists

We had multiple teams shipping to multiple clusters, and every team invented their own deployment pattern. It worked - until it didn’t. Rollbacks were manual, promotions were inconsistent, and there was no single place to reason about “what is actually running.”

This post shows the approach we use to make deployments boring, repeatable, and traceable. It mixes a short narrative with a practical guide you can apply.


What we standardised on

  • Single interface: everything deploys through ArgoCD Applications
  • Environment overlays: dev → staging → prod are explicit
  • Project + RBAC boundaries: no cross‑team bleed
  • Promotion in Git: PRs and tags, not click‑ops
  • Consistent health + rollouts: same signals everywhere

Standardised context Standardised context Standardised flow Standardised flow


Architecture (repo + layout)

App-of-Apps is the structural foundation

The root App-of-Apps Application is the single highest-leverage change you can make. One ArgoCD Application manages the rest. New team apps get added to apps/workloads/ — ArgoCD detects the new entry and creates the child application automatically. No manual ArgoCD UI work needed.

We keep a platform apps repo with a root App‑of‑Apps and a consistent layout:

platform-apps/
  apps/
    root/                # app-of-apps
    workloads/           # team apps
  overlays/
    dev/
    staging/
    prod/

Root app example:

# repo/gitops/app-of-apps/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: platform-root
  namespace: argocd
spec:
  project: platform
  source:
    repoURL: https://github.com/polarpoint-io/platform-apps
    targetRevision: main
    path: apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true


1) ArgoCD Projects + RBAC (don’t skip)

Projects define where apps can deploy and who can touch them.

# repo/gitops/standardised/projects/platform-project.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: platform
  namespace: argocd
spec:
  sourceRepos:
    - https://github.com/polarpoint-io/platform-apps
  destinations:
    - namespace: platform-*
      server: https://kubernetes.default.svc
  roles:
    - name: team-readonly
      policies:
        - p, proj:platform:team-readonly, applications, get, platform/*, allow
    - name: team-admin
      policies:
        - p, proj:platform:team-admin, applications, *, platform/*, allow

Why it matters: without Projects, every app can deploy anywhere. That’s how drift becomes security incidents.


2) Environment overlays (dev/staging/prod)

Keep environment diffs explicit and small. Example values:

# repo/gitops/standardised/overlays/values-dev.yaml
replicaCount: 1
resources:
  limits:
    cpu: 200m
    memory: 256Mi
# repo/gitops/standardised/overlays/values-prod.yaml
replicaCount: 3
resources:
  limits:
    cpu: 1000m
    memory: 1Gi

Rule: if it can’t be expressed as an overlay, it doesn’t get deployed.


3) Promotion flow (Git, not clicks)

The click-ops promotion tell

If your promotion process involves someone running kubectl apply or clicking "sync" in the ArgoCD UI without a corresponding Git change, that promotion has no audit trail, no review, and no easy rollback. If you can't git log the promotion, it's click-ops. The ArgoCD app definitions in this post target branches and tags — promotions are commits.

We promote with branches/tags and ArgoCD Applications that point to them:

  • maindev
  • release/*staging
  • prodproduction (protected)

Example dev/staging/prod apps:

# repo/gitops/standardised/promotion/argocd-app-dev.yaml
spec:
  source:
    targetRevision: main
    helm:
      valueFiles: [values-dev.yaml]

# repo/gitops/standardised/promotion/argocd-app-staging.yaml
spec:
  source:
    targetRevision: release/v1.2.3
    helm:
      valueFiles: [values-staging.yaml]
# repo/gitops/standardised/promotion/argocd-app-prod.yaml
spec:
  source:
    targetRevision: prod
    helm:
      valueFiles: [values-prod.yaml]

4) Health, rollouts, and sync order

We standardise sync waves and health checks so dependencies come up in order.

# repo/gitops/standardised/health/sync-waves.yaml
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "1"

Standard rules: - readiness/liveness probes on all deployments - consistent timeouts + retries - health overrides for CRDs if needed


Operational impact

  • Deployments are auditable Git changes
  • Rollbacks are a single commit revert
  • Drift is visible instead of hidden
  • Teams still ship fast - but on rails

Walkthrough files

Apply this: start with the App-of-Apps pattern

The single highest-leverage change you can make to an ad-hoc ArgoCD setup is introducing an App-of-Apps root. One ArgoCD Application manages the rest. New team apps get added to apps/workloads/ — no manual ArgoCD UI work. The root app detects the new directory entry and creates the child application automatically.

  • repo/gitops/app-of-apps/root-app.yaml
  • repo/gitops/standardised/projects/platform-project.yaml
  • repo/gitops/standardised/overlays/values-dev.yaml
  • repo/gitops/standardised/overlays/values-staging.yaml
  • repo/gitops/standardised/overlays/values-prod.yaml
  • repo/gitops/standardised/promotion/argocd-app-dev.yaml
  • repo/gitops/standardised/promotion/argocd-app-staging.yaml
  • repo/gitops/standardised/promotion/argocd-app-prod.yaml
  • repo/gitops/standardised/health/sync-waves.yaml

Frequently asked questions

How do you handle rollbacks in this setup?

Rollback is a Git revert. Update the targetRevision in the staging or prod ArgoCD Application to point to the previous good tag (release/v1.2.2 instead of release/v1.2.3), open a PR, merge. ArgoCD syncs to the reverted version. The entire rollback decision is in Git history.

What's the right sync wave number for dependencies vs workloads?

Common convention: infrastructure (CRDs, namespaces, secrets) at wave 0 or 1, platform services (cert-manager, ESO, ingress) at wave 2–5, workloads at wave 10+. Leave gaps between waves so you can insert new dependencies without renumbering everything. The exact numbers matter less than the relative ordering.

How do you enforce that teams use the standard overlay structure?

Kyverno policies or OPA gatekeeper rules that validate the presence of required Helm value files and reject Applications targeting non-standard paths. Combined with the Projects RBAC (teams can only deploy to their own namespaces), the structure is enforced at both the Git level (PR reviews) and the cluster level (policy engine).

Can you use Kustomize overlays instead of Helm values for environment differences?

Yes — Kustomize and Helm are both first-class in ArgoCD. The structure is the same: a base/ with the canonical manifest and overlays/dev/, overlays/staging/, overlays/prod/ directories with patches. The ArgoCD Application points to the overlay path for each environment. Choose whichever your team is more comfortable maintaining.

How do you track which version is actually running in production?

ArgoCD's UI and CLI show the current targetRevision and the git SHA of the last synced commit. For a more durable audit trail, add ArgoCD to your observability stack: the argocd_app_info metric includes revision labels. A Grafana dashboard showing current revision by app and environment gives you a fleet-wide deployment snapshot at a glance.

Demystifying Model Context Protocol (MCP): AI Gets Smarter About Context

Banner image Banner image

Ask an LLM to help you debug a Kubernetes incident. It does a decent job. Then, five minutes later, ask it a follow-up question — and watch it answer as if the previous conversation never happened.

That's not a bug in the model. That's the fundamental architectural problem with how most AI tools are wired up. Every prompt starts fresh. Decisions evaporate. Context has to be re-established from scratch every single time. At some point you stop thinking of the AI as a collaborator and start treating it like a very fast search engine that needs to be told the same things repeatedly.

Model Context Protocol (MCP) is the attempt to fix that. It's an open standard — originally proposed by Anthropic, now gaining adoption across the industry — that defines a common way for AI models and the tools around them to share, persist, and prioritise context. The goal is simple: make AI assistants behave less like stateless chatbots and more like reliable operators that actually remember what's going on.

Why you should use External Secrets Operator with ArgoCD

Banner image Banner image

Cloud‑native patterns: Why you should use External Secrets Operator with ArgoCD

There's a moment every GitOps team hits where someone asks the obvious question: "where do the secrets go?"

The rest of the configuration is in Git. It's version-controlled, reviewable, auditable. But you can't put secrets there. So they end up... somewhere else. Sometimes it's a manual kubectl create secret that nobody documents. Sometimes it's base64-encoded values tucked into Helm values files with a comment that says "TODO: fix this properly". Sometimes it's a mix of both, spread across three clusters, maintained by different people who've all developed their own workarounds.

External Secrets Operator (ESO) is the "fix this properly" solution. It connects ArgoCD's GitOps workflow to your secret manager of choice — AWS Secrets Manager, Google Secret Manager, HashiCorp Vault, Azure Key Vault — so secrets are pulled automatically at deploy time, never stored in Git, and rotated without touching your GitOps configuration.