MinhVo

Minh Vo

rss feed

Slaying code & making it lit fr fr 🔥 tagline

Hey there 👋 I'm an AI Engineer with 7 years of experience building scalable web and mobile applications. Currently at Neurond AI (May 2025 — present), architecting an Enterprise AI Assistant Platform with multi-tenant RAG on pgvector, multi-provider LLM orchestration, and Azure-native infrastructure. Previously spent 5+ years at SNAPTEC (Sep 2019 — Apr 2025), leading SaaS themes, admin dashboards, and e-commerce platforms — earned the Hero of the Year award in 2021. I specialize in TypeScript, React, Next.js, and AI-Native engineering with Claude Code and Cursor.bio

Back to blogs

GitOps: Declarative Infrastructure with ArgoCD and Flux

Implement GitOps: declarative config, automated sync, drift detection, and rollback.

GitOpsArgoCDFluxDevOpsKubernetes

By MinhVo

Introduction

GitOps represents a paradigm shift in how we manage infrastructure and application deployments. At its core, GitOps uses Git as the single source of truth for declarative infrastructure and application configurations. Instead of manually applying changes to clusters or running imperative deployment scripts, GitOps operators continuously compare the desired state defined in Git with the actual state of your cluster and automatically reconcile any differences. This approach brings the same benefits to operations that version control brought to code: auditability, collaboration, rollback capability, and automation.

The concept of GitOps was coined by Weaveworks in 2017 and has since become one of the most widely adopted operational frameworks in the cloud-native ecosystem. The CNCF established the OpenGitOps project to formalize the principles and ensure vendor-neutral governance. What makes GitOps compelling is that it solves a fundamental problem in infrastructure management: the gap between what you think is deployed and what is actually running. Traditional CI/CD pipelines push changes to clusters, but they provide no mechanism to detect or correct drift when someone runs a manual kubectl command. GitOps closes this gap by making the Git repository the authoritative source and continuously enforcing its state.

The two leading GitOps tools—ArgoCD and Flux—implement this pattern in complementary ways. ArgoCD provides a rich web UI and powerful sync capabilities with fine-grained control over how and when changes are applied. Flux takes a more Kubernetes-native approach, running as a set of controllers within the cluster and using Custom Resource Definitions (CRDs) for configuration. Both tools watch Git repositories for changes and automatically deploy the declared state, but they differ in their architecture, UI, and advanced features. Many organizations use one or the other, and some use both for different aspects of their infrastructure.

The adoption of GitOps has accelerated dramatically as organizations scale their Kubernetes deployments. Companies like Adobe, Autodesk, and Volvo have publicly shared their GitOps journeys, managing thousands of applications across hundreds of clusters. The pattern has proven particularly valuable for regulated industries where audit trails and compliance requirements demand complete visibility into who changed what, when, and why. By treating infrastructure changes as code reviews through pull requests, GitOps naturally enforces the separation of duties and approval workflows that compliance frameworks require.

This guide provides a deep dive into implementing GitOps with both ArgoCD and Flux. We cover the fundamental GitOps principles, set up each tool from scratch, configure multi-environment deployments, implement drift detection and automated rollback, and show production patterns for managing secrets, handling Helm charts, and scaling to multi-cluster environments. By the end, you will be able to implement a complete GitOps workflow for your Kubernetes infrastructure.

GitOps workflow architecture

Understanding GitOps: Core Concepts

GitOps is built on four core principles that together create a robust, auditable, and automated deployment system. Understanding these principles is essential before diving into tooling specifics. These principles were formalized by the OpenGitOps project under the CNCF, and they provide a framework for evaluating any GitOps tool or workflow.

Principle 1: Declarative Configuration

All system configuration is declared in a Git repository using YAML, JSON, or Helm charts. The desired state of every resource—deployments, services, ConfigMaps, secrets, network policies—is explicitly defined. There is no imperative "how" in the repository, only "what." This declarative approach has profound implications for debugging and troubleshooting. When something goes wrong, you can diff the Git history to see exactly what changed, rather than trying to reconstruct the sequence of imperative commands that led to the current state. Declarative configuration also enables powerful tooling: linters can validate manifests before they're applied, policy engines can enforce organizational standards, and cost estimators can predict the financial impact of infrastructure changes before they're deployed.

# Declarative: what we want (GitOps)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-server
  template:
    metadata:
      labels:
        app: api-server
    spec:
      containers:
        - name: api
          image: ghcr.io/myorg/api:v2.1.0
          ports:
            - containerPort: 3000
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"

Principle 2: Version Controlled and Immutable

Every change to the system is tracked in Git history. Rollback is as simple as reverting a commit. The Git commit hash becomes an immutable identifier for any state of the system, enabling precise auditing and reproducibility. This immutability is crucial for compliance and incident response. During a production incident, you can instantly determine what changed by looking at the most recent commits. If the fix is to revert, you simply run git revert <hash> and the GitOps operator handles the rest. This is a dramatic improvement over traditional deployment models where rollback often requires redeploying a previous artifact version and hoping that the database schema changes are backward compatible. With GitOps, the entire system state—including configuration, resource limits, and environment variables—is captured in a single commit.

Principle 3: Pulled Automatically

GitOps agents (ArgoCD or Flux) pull changes from Git repositories rather than having changes pushed to them. This pull-based model is more secure because the cluster does not need to accept incoming connections from CI systems, and it naturally handles authentication to the Git repository. The pull model also improves resilience: if the CI system goes down, the GitOps operator continues functioning independently. In a push-based CI/CD model, a CI outage means no deployments can happen. With GitOps, the deployment mechanism is decoupled from the build pipeline, providing an additional layer of reliability. The operator polls the Git repository at configurable intervals and applies changes when it detects differences between the declared and actual state.

Principle 4: Continuously Reconciled

The GitOps agent continuously compares the desired state in Git with the actual state in the cluster. If someone manually modifies a resource in the cluster (drift), the agent detects the difference and restores the desired state. This self-healing behavior ensures consistency. Continuous reconciliation is what separates GitOps from simple Git-triggered deployments. In a typical CI/CD pipeline, the deployment happens once when the pipeline runs, and subsequent changes to the cluster go undetected. With GitOps, the reconciliation loop runs continuously, typically every 3 to 5 minutes, ensuring that the cluster state converges to the desired state even after manual interventions, node failures, or external modifications.

Kubernetes cluster management

Architecture and Design Patterns

Repository Structure for GitOps

A well-organized Git repository is the foundation of a successful GitOps implementation. There are two main approaches: the single-repo (monorepo) approach and the multi-repo approach. The choice depends on team size, deployment complexity, and access control requirements. For small to medium teams managing fewer than 20 applications, a monorepo provides simplicity and a single place to understand the entire system state. For larger organizations with multiple teams, separate repositories per team or domain reduce merge conflicts and enable independent deployment cadences. The key insight is that the repository structure should mirror your organizational structure—this is Conway's Law applied to infrastructure.

Many teams also adopt a two-repository model: one repository for application source code and another for Kubernetes manifests. This separation ensures that application developers can iterate on code without worrying about deployment configuration, while platform engineers can manage infrastructure independently. The image tag in the manifests repository is typically updated by a CI pipeline when a new application version is built, creating a clean boundary between build and deploy.

# Single-repo structure (good for smaller teams)
infrastructure/
├── base/                          # Shared base configurations
│   ├── api-server/
│   │   ├── deployment.yaml
│   │   ├── service.yaml
│   │   └── kustomization.yaml
│   └── database/
│       ├── statefulset.yaml
│       └── kustomization.yaml
├── overlays/                      # Environment-specific overrides
│   ├── development/
│   │   ├── kustomization.yaml
│   │   └── patches/
│   ├── staging/
│   │   ├── kustomization.yaml
│   │   └── patches/
│   └── production/
│       ├── kustomization.yaml
│       └── patches/
└── clusters/                      # Per-cluster ArgoCD/Flux config
    ├── dev-cluster/
    └── prod-cluster/

Application of Applications Pattern

ArgoCD's "App of Apps" pattern uses a parent Application that points to a directory containing child Application manifests. This enables managing multiple applications from a single entry point. The pattern is essential for platform teams that manage dozens or hundreds of applications. Instead of creating and maintaining individual ArgoCD Application resources for each service, you define them in a single directory and let the parent application manage them. When a new service is added to the platform, the team simply adds a new Application manifest to the directory—the parent application automatically detects it and creates the corresponding ArgoCD Application. This pattern also simplifies cluster bootstrapping: you can set up an entire production environment by pointing ArgoCD at a single Git directory that contains all the Application definitions.

The App of Apps pattern can be extended to a hierarchy of applications, where sub-parent applications manage groups of related services. For example, you might have a "monitoring" parent application that manages Prometheus, Grafana, Loki, and Tempo, and a "data-services" parent that manages PostgreSQL, Redis, and Kafka. This hierarchical organization mirrors the logical grouping of services and makes it easy to understand which applications belong to which domain.

Step-by-Step Implementation

Setting Up ArgoCD

ArgoCD is installed as a set of Kubernetes controllers and a web UI. Let us set it up from scratch. ArgoCD consists of several components that work together: the API server handles authentication and serves the web UI, the application controller performs reconciliation and sync operations, the repo server clones Git repositories and generates Kubernetes manifests (supporting plain YAML, Helm, Kustomize, and Jsonnet), and the Redis instance provides caching for repository data. For production deployments, you should configure high availability by running multiple replicas of each component and using an external Redis instance or Redis Sentinel for cache redundancy.

# Install ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
 
# Get the initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
 
# Access the UI (port-forward for development)
kubectl port-forward svc/argocd-server -n argocd 8080:443
 
# Login via CLI
argocd login localhost:8080
argocd account update-password

ArgoCD Application Definition

# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-server
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/infrastructure.git
    targetRevision: main
    path: overlays/production
    kustomize:
      images:
        - ghcr.io/myorg/api=v2.1.0
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true         # Delete resources removed from Git
      selfHeal: true       # Revert manual cluster changes
      allowEmpty: false    # Prevent deploying nothing
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

ArgoCD App of Apps Pattern

# Parent Application that manages all child applications
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: all-apps
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/infrastructure.git
    targetRevision: main
    path: clusters/production/applications
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Setting Up Flux

Flux v2 is installed as a set of Kubernetes controllers managed through CRDs. Unlike ArgoCD's monolithic installation, Flux is composed of five specialized controllers: source-controller manages Git and Helm repositories, kustomize-controller applies Kustomize overlays and plain YAML, helm-controller manages Helm chart releases, notification-controller handles alerting and external event sources, and image-automation-controller automates image tag updates. This modular architecture means you can install only the controllers you need, reducing resource overhead and attack surface. Flux is also designed to be bootstrapped directly from Git—the bootstrap command creates the initial configuration in your Git repository and configures the controllers to reconcile from it, ensuring that even the Flux installation itself is managed through GitOps.

# Install Flux CLI
brew install fluxcd/tap/flux
 
# Bootstrap Flux (connects to your GitHub repo)
flux bootstrap github \
  --owner=myorg \
  --repository=infrastructure \
  --branch=main \
  --path=clusters/production \
  --personal

Flux Source and Kustomization

# gitrepository.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: infrastructure
  namespace: flux-system
spec:
  interval: 1m
  url: https://github.com/myorg/infrastructure.git
  ref:
    branch: main
  secretRef:
    name: git-credentials
 
---
# kustomization.yaml (Flux CRD, not Kustomize)
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: api-server
  namespace: flux-system
spec:
  interval: 5m
  path: ./overlays/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: infrastructure
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: api-server
      namespace: production
  timeout: 3m

Flux Helm Release

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: prometheus
  namespace: monitoring
spec:
  interval: 15m
  chart:
    spec:
      chart: prometheus
      version: "25.x"
      sourceRef:
        kind: HelmRepository
        name: prometheus-community
      interval: 1h
  values:
    server:
      persistentVolume:
        size: 50Gi
    alertmanager:
      enabled: true

GitOps deployment pipeline visualization

Real-World Use Cases

Use Case 1: Multi-Environment Promotion

Managing multiple environments (dev, staging, production) with GitOps requires a clear promotion strategy. Changes flow from dev to staging to production through Git operations. The most common pattern uses branch-based promotion: developers push to a develop branch that automatically deploys to the development environment, merge to main for staging deployment, and create a release tag for production. This model leverages Git's native branching and tagging mechanisms to control the deployment pipeline, providing a natural audit trail of which changes have been promoted to each environment.

An alternative approach is directory-based promotion, where each environment has its own directory in the repository and changes are promoted by copying manifests between directories via pull request. This approach is more explicit about what is deployed to each environment and makes it easy to see the differences between environments by diffing the directories. However, it requires more discipline to keep the directories synchronized and avoid configuration drift between environments. Many teams combine both approaches: using branches for the development pipeline and directories for the staging-to-production promotion, where the production directory is updated through an approved pull request that includes the specific image tag and any production-specific configuration overrides.

# Development: auto-sync from develop branch
# Application spec:
spec:
  source:
    targetRevision: develop
  syncPolicy:
    automated:
      selfHeal: true
 
# Staging: auto-sync from main branch
# Merging to main deploys to staging
spec:
  source:
    targetRevision: main
  syncPolicy:
    automated:
      selfHeal: true
 
# Production: manual sync from release tags
# Creating a tag deploys to production after approval
spec:
  source:
    targetRevision: v2.1.0
  syncPolicy:
    syncOptions:
      - PruneLast=true
    # No automated sync — requires manual approval

Use Case 2: Drift Detection and Auto-Remediation

When a developer manually changes a resource in the cluster using kubectl, GitOps detects and reverts the change. Drift detection is one of the most valuable features of GitOps, addressing a problem that has plagued operations teams for decades: configuration drift. In traditional environments, configuration drift accumulates gradually as engineers make manual changes during incident response, apply temporary fixes that become permanent, or run ad-hoc scripts that modify cluster state. Over time, the production environment diverges significantly from what is documented, making it impossible to reproduce the environment from scratch.

GitOps solves this problem definitively. When the reconciliation loop detects that the actual cluster state differs from the declared state in Git, it immediately flags the resource as OutOfSync. If self-heal is enabled, the operator automatically restores the resource to its declared state within minutes. This creates a powerful incentive structure: engineers learn quickly that manual changes are futile because they will be reverted, which drives all changes through Git where they can be reviewed and tracked. Over time, this feedback loop eliminates configuration drift entirely and creates a culture where Git is the single source of truth.

The drift detection capability also serves as an early warning system for security incidents. If an attacker gains access to a cluster and modifies a deployment to run a cryptominer, the GitOps operator will detect the unauthorized change and revert it. While this is not a complete security solution, it provides an additional layer of defense that catches a specific class of attack that would go undetected in traditional CI/CD setups.

# A developer manually scales the deployment
kubectl scale deployment api-server -n production --replicas=10
 
# ArgoCD detects drift within seconds and shows OutOfSync status
argocd app get api-server
# Status: OutOfSync
# api-server (apps/v1/Deployment) - drift detected:
#   spec.replicas: 10 (cluster) != 3 (git)
 
# If selfHeal is enabled, ArgoCD automatically reverts to 3 replicas
# If selfHeal is disabled, a manual sync restores the desired state

Use Case 3: Automated Rollback

When a deployment causes health check failures, GitOps provides a clean rollback path.

# ArgoCD sync waves with health checks
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-server
spec:
  syncPolicy:
    automated:
      selfHeal: true
    retry:
      limit: 3
      backoff:
        duration: 10s
        factor: 2
        maxDuration: 1m
 
# Rollback: revert the commit
git revert HEAD
git push origin main
# ArgoCD automatically deploys the reverted version

Use Case 4: Secret Management with SOPS

Managing secrets in GitOps requires encryption. Mozilla SOPS encrypts secret values while keeping the structure readable. SOPS supports multiple encryption backends including AWS KMS, GCP KMS, Azure Key Vault, age, and PGP. The key advantage of SOPS over alternatives like Sealed Secrets is that SOPS-encrypted files remain valid YAML—only the values are encrypted, not the keys. This means you can see what keys exist in a secret (like DATABASE_URL and API_KEY) without seeing their values, which is useful for code review and debugging. SOPS also supports per-value encryption, allowing you to encrypt individual keys within a file while leaving others in plain text. This granularity is valuable when some values are sensitive (passwords) while others are not (configuration flags).

Integrating SOPS with Flux is straightforward through the SOPS KSOPS Kustomize plugin or the Flux built-in SOPS decryption support. When Flux encounters a SOPS-encrypted file during reconciliation, it decrypts it using the configured keys and applies the resulting Kubernetes Secret to the cluster. The decryption keys are stored as Kubernetes Secrets in the Flux namespace, accessible only to the Flux controllers. This means the decryption capability never leaves the cluster boundary, and the encrypted secrets in Git are useless without access to the cluster's decryption keys.

# Encrypted secret (SOPS)
apiVersion: v1
kind: Secret
metadata:
  name: api-secrets
  namespace: production
type: Opaque
data:
  DATABASE_URL: ENC[AES256_GCM,data:encrypted_value,tag:auth_tag,type:str]
  API_KEY: ENC[AES256_GCM,data:encrypted_value,tag:auth_tag,type:str]
sops:
  kms:
    - arn: arn:aws:kms:us-east-1:123456789:key/abc-123

Best Practices for Production

Production GitOps deployments require careful attention to security, performance, and operational maturity. The following practices are drawn from organizations that have successfully scaled GitOps to hundreds of clusters and thousands of applications. Each practice addresses a specific failure mode or operational challenge that teams encounter as they move from proof-of-concept to production.

  1. Use Kustomize overlays for environments: Define base configurations and use overlays to customize per environment. This reduces duplication and ensures consistency across environments. Kustomize's patching mechanism allows you to override specific values for each environment without duplicating the entire manifest set. Structure your overlays to reflect your promotion pipeline: development overlays might disable resource limits for faster iteration, while production overlays enforce strict security contexts and resource quotas.

  2. Enable self-heal and prune: Automated sync with self-heal prevents configuration drift. Prune ensures resources removed from Git are deleted from the cluster. Without pruning, removing an application from Git leaves orphaned resources in the cluster, which can cause confusion and waste resources. However, be cautious with pruning in production—consider using sync waves to ensure dependent resources are not deleted before their dependents.

  3. Use sync waves for ordered deployments: ArgoCD sync waves let you control the order in which resources are applied, ensuring dependencies are created before dependent resources. Assign wave numbers using the argocd.argoproj.io/sync-wave annotation. Lower numbers are applied first. For example, namespaces get wave 0, ConfigMaps and Secrets get wave 1, Deployments get wave 2, and Services get wave 3. This prevents the common error of a Deployment referencing a ConfigMap that hasn't been created yet.

  4. Implement RBAC for GitOps: Restrict who can modify GitOps repository configurations. Use branch protection rules and required reviews for production changes. In ArgoCD, use Projects to scope applications to specific namespaces and repositories. In Flux, use namespaced controllers to limit the blast radius of configuration changes. Never allow direct cluster access for deployment—require all changes to flow through Git.

  5. Monitor sync status: Set up alerts for failed syncs, degraded health, and configuration drift. Both ArgoCD and Flux expose metrics for Prometheus. Key metrics to monitor include sync duration, sync failure count, application health status, and reconciliation loop duration. Set up alerts for applications that remain OutOfSync for more than 15 minutes, as this typically indicates a configuration error that prevents successful deployment.

  6. Use separate repositories for app code and infra config: Application source code and Kubernetes manifests should be in different repositories to separate concerns and enable independent versioning. The infrastructure repository should be owned by the platform team, while application teams own their source code repositories. Use CI pipelines to update image tags in the manifests repository when new application versions are built.

  7. Encrypt secrets with SOPS or Sealed Secrets: Never commit plain-text secrets to Git. Use SOPS, Sealed Secrets, or External Secrets Operator for encrypted secret management. SOPS encrypts the values while keeping the YAML structure readable, making it easy to see what keys exist without exposing their values. Sealed Secrets provide per-cluster encryption, ensuring that secrets encrypted for one cluster cannot be decrypted by another.

  8. Test configurations with dry-run: Before merging changes to the main branch, use ArgoCD's diff feature or Flux's dry-run to preview what will change. Integrate CI checks that run kustomize build and validate manifests against schemas using tools like kubeval or kubeconform. This catches syntax errors and schema violations before they reach the cluster.

Common Pitfalls and Solutions

Implementing GitOps successfully requires avoiding several common traps that can undermine the benefits of the approach. These pitfalls are based on real-world experiences from teams that have adopted GitOps at scale.

PitfallImpactSolution
Storing plain-text secrets in GitSecurity breach, credential leaksUse SOPS, Sealed Secrets, or External Secrets Operator
No resource limits in manifestsResource exhaustion, noisy neighborAlways set requests and limits in deployment specs
Syncing too frequentlyExcessive API server loadSet reasonable sync intervals (5-15 minutes for most apps)
No health checks configuredDeploying broken applications that never recoverConfigure liveness, readiness, and startup probes
Single monolithic repo for everythingSlow sync, complex merge conflictsSeparate concerns: base configs, overlays, cluster configs
Ignoring resource orderingDeployments fail due to missing dependenciesUse sync waves (ArgoCD) or dependsOn (Flux)
No rollback strategyExtended downtime during failed deploymentsAlways test git revert in staging before production
Over-relying on self-healMasking underlying infrastructure issuesMonitor drift frequency as an indicator of systemic problems
Missing notificationsFailed syncs go unnoticed for hoursConfigure alerting for sync failures and degraded health
No branch protectionAccidental production deployments from force-pushesEnable branch protection with required reviews and status checks

Performance Optimization

Scaling GitOps to hundreds of applications requires careful attention to performance. Both ArgoCD and Flux have performance characteristics that change significantly as the number of managed resources grows. Understanding these characteristics helps you design a GitOps architecture that performs well at scale.

For ArgoCD, the primary performance bottleneck is the repository server, which must clone and render manifests for each application during every reconciliation cycle. With hundreds of applications pointing to the same repository, the repo server can become overwhelmed. Mitigation strategies include using Git shallow clones (reducing the data transferred), caching rendered manifests, and increasing repo server resources. The ArgoCD ApplicationSet controller can also help by generating applications from a template, reducing the number of unique Git repository paths that need to be rendered.

For Flux, performance tuning focuses on reconciliation intervals and garbage collection frequency. Setting intervals too short causes excessive API server load, while setting them too long delays the detection of drift. A good starting point is 5 minutes for most applications, with shorter intervals (1-2 minutes) for critical services and longer intervals (10-15 minutes) for infrastructure components that change infrequently. The Flux garbage collector should be configured to prune orphaned resources, but be aware that aggressive pruning with large resource sets can cause API server throttling.

# ArgoCD: optimize sync for large clusters
apiVersion: argoproj.io/v1alpha1
kind: ArgoCD
metadata:
  name: argocd
  namespace: argocd
spec:
  controller:
    processors:
      operation: 10
      status: 20
    resources:
      requests:
        memory: "1Gi"
        cpu: "500m"
      limits:
        memory: "2Gi"
        cpu: "1000m"
 
# Flux: optimize reconciliation intervals
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: api-server
spec:
  interval: 10m           # Reconcile every 10 minutes
  timeout: 3m             # Timeout after 3 minutes
  retryInterval: 1m       # Retry failed syncs after 1 minute

Comparison with Alternatives

Choosing between ArgoCD and Flux is one of the first decisions teams face when adopting GitOps. Both tools implement the same core principles but differ in their architecture, user experience, and ecosystem integration. ArgoCD is the more popular choice for teams that value a rich web UI and fine-grained sync control. Its visualization capabilities—showing resource trees, dependency graphs, and sync status in a single dashboard—make it easy for operators to understand the state of their deployments at a glance. Flux, on the other hand, appeals to teams that prefer a Kubernetes-native approach where all configuration is done through CRDs rather than a separate UI layer.

The choice often depends on your existing tooling and team structure. If your team is already comfortable with Kubernetes CRDs and prefers to manage everything through kubectl or Git, Flux's approach feels natural. If your team includes operators who prefer visual dashboards and want built-in support for sync waves, resource tracking, and application grouping, ArgoCD is the better fit. Many organizations start with one tool and add the other for specific use cases—for example, using ArgoCD as the primary GitOps platform while adopting Flux's image automation controllers for automatic image tag updates.

FeatureArgoCDFlux v2SpinnakerJenkins X
ArchitectureCentralized (server + UI)Distributed (in-cluster controllers)Centralized serverIn-cluster
Web UIRich, full-featuredWeave GitOps UI or Grafana dashboardsRichLimited
Multi-tenancyProjects with RBACNamespaced controllersAccountsTeams
Helm supportNativeHelmRelease CRDNativeNative
Kustomize supportNativeKustomization CRDPluginNative
NotificationNotification controllerAlert and Provider CRDsSpinnaker eventsLighthouse
Learning curveModerateModerateSteepModerate
Best forTeams wanting UI + CLIKubernetes-native teamsLarge enterpriseCloud-native CI/CD

Advanced Patterns

Multi-Cluster GitOps

Managing multiple clusters from a single control plane requires careful repository structure and cluster registration. Multi-cluster GitOps is one of the most powerful patterns for organizations operating at scale. Instead of managing each cluster independently, you register all clusters with a central ArgoCD instance (or Flux control plane) and define which applications target which clusters. This approach enables consistent configuration across clusters while allowing cluster-specific customization through overlays.

The most common multi-cluster architecture uses a management cluster that runs ArgoCD or Flux and targets one or more workload clusters. The management cluster is typically the most restricted environment, with access limited to platform engineers. Workload clusters are registered with the management cluster using credentials that have limited permissions, following the principle of least privilege. Application definitions specify which cluster and namespace they target, and the GitOps operator handles deployment to the correct destination.

For disaster recovery scenarios, multi-cluster GitOps provides a significant advantage: you can stand up a replacement cluster by registering it with the management cluster and letting the GitOps operator deploy the entire application stack from the Git repository. This eliminates the need for separate disaster recovery runbooks and ensures that the recovery state matches the last known good configuration.

# ArgoCD cluster secret
apiVersion: v1
kind: Secret
metadata:
  name: production-cluster
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
  name: production-cluster
  server: https://prod-cluster.example.com:6443
  config: |
    {
      "bearerToken": "...",
      "tlsClientConfig": {
        "insecure": false,
        "caData": "..."
      }
    }
 
# Application targeting specific cluster
spec:
  destination:
    server: https://prod-cluster.example.com:6443
    namespace: production

Progressive Delivery with Argo Rollouts

GitOps naturally integrates with progressive delivery strategies like canary deployments and blue-green deployments. Argo Rollouts extends the standard Kubernetes Deployment resource with advanced deployment strategies that minimize risk during releases. Instead of replacing all pods at once, a canary deployment gradually shifts traffic from the old version to the new version, monitoring key metrics at each step. If the metrics degrade—error rate increases, latency spikes, or availability drops—the rollout automatically aborts and reverts to the previous version. This automated rollback capability, combined with GitOps, creates a deployment pipeline that is both fast and safe.

The integration with GitOps means that the rollout strategy itself is declared in Git alongside the application manifest. Changes to the canary configuration—such as the percentage of traffic routed to the new version or the duration of each pause step—go through the same pull request review process as any other infrastructure change. This makes the progressive delivery strategy auditable and reproducible, addressing a common concern with automated deployment systems where the deployment behavior is opaque.

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: api-server
spec:
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 30
        - pause: { duration: 5m }
        - setWeight: 60
        - pause: { duration: 5m }
        - setWeight: 100
      analysis:
        templates:
          - templateName: success-rate
        startingStep: 1

Testing Strategies

Testing GitOps configurations before they reach production requires a multi-layered approach. Unlike application code, Kubernetes manifests cannot be unit tested in isolation—they depend on the cluster state, external services, and the behavior of the GitOps operator itself. The testing pyramid for GitOps includes static validation (schema checking, linting), dry-run reconciliation (server-side validation), and integration testing (actual deployment to a test cluster).

Start with static validation in your CI pipeline. Tools like kubeval, kubeconform, and Datree can validate manifests against the Kubernetes JSON schemas without requiring a running cluster. Kustomize build output can be piped through these validators to catch errors early. For policy violations, run OPA/Conftest or Kyverno CLI against the rendered manifests to enforce organizational standards like requiring resource limits, prohibiting privileged containers, or mandating specific labels.

The next layer is dry-run validation using kubectl apply --dry-run=server, which sends the manifests to the API server for validation without actually creating resources. This catches issues that static validation cannot detect, such as references to non-existent namespaces or conflicting resource definitions. ArgoCD's diff feature provides a similar capability, showing exactly what changes would be applied without making them.

# Test ArgoCD applications locally with ArgoCD CLI
argocd app diff api-server --local overlays/production/
 
# Test Flux configurations with kubectl dry-run
kubectl apply -k overlays/production/ --dry-run=server
 
# Validate Kustomize builds
kustomize build overlays/production/ | kubectl apply --dry-run=client -f -
 
# Test with kustomize build and kubeval
kustomize build overlays/production/ | kubeval --strict

GitOps Beyond Kubernetes: Crossplane

GitOps principles extend beyond application deployments to infrastructure provisioning. Crossplane, a CNCF project, lets you define cloud infrastructure (databases, message queues, DNS records) as Kubernetes CRDs and manage them through GitOps. This means your entire technology stack—from the application code running in containers to the managed database services, message queues, and DNS records that support it—can be declared in Git and reconciled by the same operator. The implications are transformative: infrastructure changes go through the same pull request and review process as application changes, and drift detection applies to cloud resources just as it does to Kubernetes deployments.

Crossplane compositions allow you to build higher-level abstractions that encapsulate your organization's infrastructure patterns. For example, you can define a "ProductionDatabase" composite resource that automatically provisions an RDS instance with encryption enabled, automated backups configured, and monitoring alarms attached. This self-service approach empowers application teams to provision infrastructure without needing deep knowledge of cloud-specific APIs, while platform engineers retain control over security and compliance standards through the composition definitions stored in Git.

# Crossplane: Provision an RDS database via GitOps
apiVersion: rds.aws.crossplane.io/v1alpha1
kind: DBInstance
metadata:
  name: production-postgres
spec:
  forProvider:
    engine: postgres
    engineVersion: "15"
    dbInstanceClass: db.t3.medium
    masterUsername: admin
    allocatedStorage: 100
    storageEncrypted: true
    vpcSecurityGroupIds:
      - sg-12345678
  providerConfigRef:
    name: aws-provider
  writeConnectionSecretToRef:
    name: production-postgres-conn
    namespace: crossplane-system

With Crossplane, your entire infrastructure—applications and cloud resources—is declared in Git and reconciled by the same GitOps operator. This eliminates the split between "infrastructure as code" (Terraform) and "application deployment" (Kubernetes), providing a single workflow for everything.

Future Outlook

The GitOps ecosystem is maturing rapidly. The OpenGitOps project under CNCF is standardizing GitOps principles. ArgoCD and Flux continue to improve with better multi-cluster support, enhanced UI capabilities, tighter integration with progressive delivery tools, and improved performance for large-scale deployments. The GitOps model is expanding beyond Kubernetes to manage any declarative infrastructure through tools like Crossplane and Terraform Controller. The convergence of GitOps with policy engines (OPA/Gatekeeper, Kyverno) is enabling compliance-as-code where security and governance policies are enforced automatically through the same Git-based workflow.

The emergence of AI-assisted operations is beginning to influence GitOps workflows. Machine learning models can analyze deployment patterns and predict potential issues before they occur, suggesting configuration changes or flagging risky modifications during code review. Tools like Argo CD Autopilot are reducing the boilerplate required to set up GitOps, making the pattern accessible to smaller teams with less platform engineering expertise. The integration of cost management tools with GitOps repositories is also gaining traction, allowing teams to see the estimated cost impact of infrastructure changes directly in their pull requests.

As edge computing grows, GitOps is being adapted to manage distributed edge deployments where clusters may have intermittent connectivity. Flux's lightweight architecture makes it particularly well-suited for edge scenarios where the GitOps operator must function with limited resources and unreliable network connections. The future of GitOps is one where every aspect of the technology stack is declared in Git, continuously reconciled, and managed through the same automated workflow.

Conclusion

GitOps with ArgoCD and Flux transforms infrastructure management from manual, error-prone operations into automated, auditable, and self-healing systems. By storing all configuration in Git, you gain version history, easy rollback, team collaboration through pull requests, and automated deployment. The continuous reconciliation loop ensures your clusters always match the declared state, eliminating configuration drift and snowflake servers. Whether you choose ArgoCD for its rich UI and sync control or Flux for its Kubernetes-native design, the GitOps pattern fundamentally improves how you manage infrastructure at scale.

The key takeaways are: structure your Git repository with base configurations and environment overlays, enable automated sync with self-healing and pruning, encrypt secrets with SOPS or Sealed Secrets, implement health checks on all workloads, and use sync waves for ordered deployments. Whether you choose ArgoCD for its rich UI and sync control or Flux for its Kubernetes-native approach, GitOps principles will make your deployments more reliable, auditable, and scalable. Start with a single application, prove the workflow, and expand from there.