GitOps Platform Patterns
ArgoCD vs Flux
ArgoCD and Flux are both CNCF graduated projects. They solve the same problem with different philosophies.
ArgoCD provides a web UI, an API server, and a centralized model. One ArgoCD instance manages deployments across multiple clusters. The UI shows sync status, diff views, and resource health at a glance. ApplicationSets generate ArgoCD applications dynamically from git repository structure, cluster lists, or pull request events. This makes it the better choice for platform teams that need visibility across many teams and clusters.
Flux follows a decentralized model. Each cluster runs its own Flux instance that watches git repositories. There's no central UI (though Weave GitOps adds one). Flux's strength is composability. It uses separate controllers for source management, Kustomize overlays, Helm releases, and notification. You can use only the pieces you need.
For most platform teams, ArgoCD wins on developer experience and operational visibility. Flux wins on simplicity and footprint for single-cluster setups.
Repository Structure Patterns
The two common patterns are monorepo and app-per-repo. In a monorepo, all GitOps manifests live in one repository organized by environment and application. The directory structure looks like environments/production/app-a/, environments/staging/app-a/. This gives platform teams a single place to see everything and makes cross-cutting changes (updating a base image across all apps) straightforward.
App-per-repo stores each application's manifests alongside its code or in a dedicated deployment repo. This gives teams full ownership but makes it harder to enforce standards and see the global picture.
The pragmatic middle ground is a monorepo for shared infrastructure (namespaces, RBAC, network policies, platform services) and per-team repos for application manifests. ArgoCD's App of Apps pattern or ApplicationSets handle both models.
Environment Promotion
Changes should flow through environments in order: dev, staging, production. In a GitOps model, this means separate directories or branches representing each environment. When a change is validated in staging, a PR promotes it to the production directory.
Automate the promotion for non-production environments. A merge to the staging directory triggers an automated PR to production after integration tests pass. Production promotion requires a human approval on the PR. This keeps the speed of automation with a human gate where it matters.
Kustomize overlays are the cleanest way to handle environment differences. A base directory contains the common manifests. Each environment overlay patches only what differs: replica count, resource limits, environment variables, and feature flags.
Drift Detection and Reconciliation
GitOps controllers continuously compare the desired state in git with the actual state in the cluster. When someone runs kubectl edit or kubectl apply directly, the controller detects the drift and reverts the change. This is a feature, not a bug.
ArgoCD shows drift in the UI with a yellow "OutOfSync" status. You can configure it to auto-sync (immediately revert drift) or manual sync (alert but wait for human action). For production, start with manual sync and move to auto-sync after your team trusts the process.
Set the sync interval appropriately. The default 3-minute polling interval works for most teams. For faster feedback, configure webhooks from git to ArgoCD so syncs trigger immediately on merge.
Secret Management in GitOps
Raw secrets in git is an obvious security violation. Four solutions work with GitOps. Sealed Secrets encrypts secrets with a cluster-specific key. Only the cluster can decrypt them. The encrypted SealedSecret resource lives safely in git. SOPS (by Mozilla) encrypts specific values in YAML files using KMS, PGP, or age keys. This lets you see the structure of the secret in git but not the values.
External Secrets Operator doesn't put secrets in git at all. Instead, it stores an ExternalSecret resource that references a secret in Vault, AWS Secrets Manager, or GCP Secret Manager. The operator fetches the actual secret at runtime. This is the cleanest separation but adds a runtime dependency on the external secret store.
For most platform teams, External Secrets Operator is the recommended approach. It keeps secrets entirely out of git and works with whatever secret manager you already use.
Key Points
- •Git becomes the single source of truth for both infrastructure and application configuration, making every change auditable through git history
- •ArgoCD supports multi-cluster deployments natively with ApplicationSets while Flux uses a more decentralized model with one Flux instance per cluster
- •Drift detection and automatic reconciliation mean that manual kubectl changes get reverted within minutes, enforcing declared state
- •Secret management in GitOps requires tools like Sealed Secrets, SOPS, or External Secrets Operator since raw secrets cannot live in git
- •PR-based deployments give you code review for infrastructure changes, automatic preview environments, and a clear rollback path through git revert
Common Mistakes
- ✗Storing application code and GitOps manifests in the same repository, which triggers deployments on every code commit even when manifests haven't changed
- ✗Not implementing a promotion workflow so changes go directly from a developer's PR to production without passing through staging
- ✗Relying on ArgoCD auto-sync for production without understanding that it will immediately apply any change merged to the target branch