GitOps: Declarative Deployments with ArgoCD and Flux

Implement GitOps for declarative, auditable infrastructure and application deployments using ArgoCD or Flux as your deployment operator.

published: reading time: 18 min read author: GeekWorkBench

Introduction

GitOps extends DevOps practices by using Git as the single source of truth for declarative infrastructure and applications.

Core principles:

  1. Declarative description: All infrastructure and applications defined declaratively
  2. Git as source of truth: Desired state stored in Git, not in running clusters
  3. Automated synchronization: Software automatically syncs cluster state to Git state
  4. Pull-based updates: Operators pull changes from Git, not pushed by CI

Benefits:

  • Auditable: Every change recorded in git history
  • Reproducible: Environment recreation from Git is deterministic
  • Fast rollback: Revert to previous commit for instant rollback
  • Self-healing: Drift between Git and cluster automatically corrected
  • Developer-friendly: Standard git workflows for deployments

ArgoCD Architecture and Installation

ArgoCD runs as a Kubernetes controller and continuously monitors Git repositories, comparing desired state with actual cluster state.

Architecture components:


Git Repository → ArgoCD → Kubernetes Cluster
     ↑              ↓
     ←←←←diff/sync←←←←

Installation:


# Namespace install
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Or with Helm
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd -n argocd --create-namespace

ArgoCD CLI:


# Install CLI
brew install argocd

# Login (get initial password)
argocd login --insecure --username admin --password $(kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o jsonpath='{.items[0].spec.containers[0].env[?(@.name=="ARGOCD_AUTH_SECRET")].value}') localhost:8080

# Add repo
argocd repo add https://github.com/myorg/manifests --username myuser --password mytoken

# Sync application
argocd app sync myapp

ArgoCD server access:

# ingress.yaml for external access
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
spec:
  ingressClassName: nginx
  rules:
    - host: argocd.mycorp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: argocd-server
                port:
                  number: 443

Application and ApplicationSet Resources

ArgoCD defines applications that point to Git repositories containing Kubernetes manifests.

Basic Application:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/manifests.git
    targetRevision: HEAD
    path: apps/myapp/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: myapp
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Helm-based Application:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  source:
    repoURL: https://github.com/myorg/charts.git
    chart: myapp
    targetRevision: 1.2.0
    helm:
      valueFiles:
        - values-production.yaml
      parameters:
        - name: image.tag
          value: v2.1.0
      releaseName: myapp
  destination:
    server: https://kubernetes.default.svc
    namespace: myapp

ApplicationSet for GitOps automation across clusters:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-multicluster
spec:
  generators:
    - git:
        repoURL: https://github.com/myorg/manifests.git
        revision: HEAD
        directories:
          - path: clusters/production/*
    - clusters:
        selector:
          matchLabels:
            environment: production

  template:
    metadata:
      name: myapp-{{name}}
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/manifests.git
        path: apps/myapp
        targetRevision: HEAD
      destination:
        server: "{{server}}"
        namespace: myapp
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

Flux Installation and Configuration

Flux uses a different operator model with GitRepository and Kustomization resources.

Installation with Flux CLI:


# Install Flux CLI
brew install fluxcd/tap/flux

# Bootstrap to cluster
flux bootstrap github \
  --owner=myorg \
  --repository=flux-infra \
  --branch=main \
  --path=./clusters/production \
  --personal

Core Flux resources:

# GitRepository defines the source
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: myapp
  namespace: flux-system
spec:
  interval: 1m
  url: https://github.com/myorg/manifests.git
  ref:
    branch: main
  secretRef:
    name: git-credentials
  ignore: |
    # Ignore documentation in subfolders
    /**/*.md
# Kustomization defines what to apply
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: myapp
  namespace: flux-system
spec:
  interval: 5m
  path: ./apps/myapp/overlays/production
  prune: true
  wait: true
  sourceRef:
    kind: GitRepository
    name: myapp
  targetNamespace: myapp
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: myapp
      namespace: myapp

Multi-environment with Flux:

# Production kustomization depends on staging
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: myapp-staging
spec:
  dependsOn:
    - name: myapp-base
  # ...

---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: myapp-production
spec:
  dependsOn:
    - name: myapp-staging # Wait for staging to be healthy
  # ...

Drift Detection and Correction

GitOps operators continuously monitor and correct drift.

ArgoCD drift detection:


# Check application health and sync status
argocd app get myapp

# Show diff between Git and cluster
argocd app diff myapp

# Manually sync after resolving drift
argocd app sync myapp

ArgoCD health assessment:

# Custom health check for custom resources
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas # Ignore replica count differences

Flux drift handling:


# Suspend reconciliation
flux suspend kustomization myapp

# Resume reconciliation
flux resume kustomization myapp

# Force reconciliation
flux reconcile kustomization myapp --with-source

GitOps with Helm and Kustomize

Both tools work well with GitOps operators.

HelmRelease with ArgoCD:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  source:
    chart: nginx
    repoURL: https://charts.bitnami.com/bitnami
    targetRevision: 15.0.0
    helm:
      releaseName: myapp
      values:
        replicaCount: 3
        service:
          type: LoadBalancer

HelmRelease with Flux:

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: myapp
spec:
  interval: 1h
  chart:
    spec:
      chart: myapp
      version: 1.0.0
      sourceRef:
        kind: HelmRepository
        name: myorg-charts
  values:
    replicaCount: 3
  install:
    crds: Create
  upgrade:
    crds: CreateReplace

Kustomize with ArgoCD:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  source:
    repoURL: https://github.com/myorg/manifests.git
    path: apps/myapp/overlays/production
    kustomize:
      commonLabels:
        app.kubernetes.io/managed-by: argocd
      images:
        - name: myapp
          newTag: v2.1.0

When to Use / When Not to Use

When GitOps makes sense

GitOps earns its keep when you need audit trails for infrastructure changes. If your team has ever spent hours tracking down which change caused a production incident, Git history solves that by default. Every deployment is a commit, every rollback is a git revert.

Use GitOps when you manage multiple clusters. Manually keeping staging, production, and disaster-recovery clusters in sync is error-prone. GitOps operators ensure all clusters converge to the same desired state without human intervention.

GitOps also helps when you need compliance documentation. Regulated industries require evidence that production matches approved configurations. Git provides that automatically — the commit hash is the audit log entry.

When to stick with CI-driven deployments

If your team is small and your infrastructure changes infrequently, GitOps adds operational overhead without much benefit. The operator needs maintaining, credentials need rotating, and drift detection needs monitoring. For a single cluster with two engineers, a well-written CI pipeline can do the job without the extra components.

GitOps also does not play well with stateful workloads that modify their own state. A database that accepts writes directly cannot be fully GitOps-controlled because the operator cannot distinguish intentional changes from drift.

GitOps Tool Selection Flow


flowchart TD
    A[Team needs GitOps?] --> B{Multiple clusters?}
    B -->|Yes| C{ArgoCD vs Flux?}
    B -->|No| D[Stick with CI-driven deploys]
    C -->|Need UI and ApplicationSets| E[ArgoCD]
    C -->|Need fine-grained Flux CRDs| F[Flux]
    A --> G{Need audit trail?}
    G -->|Yes| B
    G -->|No| D

ArgoCD vs Flux Comparison

Both operators implement GitOps, but they differ in philosophy and capability.

AspectArgoCDFlux
UIBuilt-in web UICLI and external dashboards only
Multi-clusterApplicationSets for scaleCluster API bootstrap
Learning curveSimpler conceptsFlux CD v2 operators are more granular
Extension modelPlugins and config managementCRD-based operators
Helm supportNative with value overridesHelmRelease CRD
GitHub integrationTight with ApplicationsReconciler pattern
MaturityCNCF graduatedCNCF graduated

Choose ArgoCD when you want a UI for non-Kubernetes engineers to view deployment status. Choose Flux when you need deep integration with Kubernetes primitives or when you prefer everything as a custom resource.

Production Failure Scenarios

Common GitOps Failures

FailureImpactMitigation
Git credentials expireOperator stops syncing, drift accumulatesUse service accounts with token rotation
PR merged with bad YAMLBroken manifests deployed to productionEnable diff-before-sync, require reviews
Large manifest causes timeoutApplication stuck in progressing stateSplit into smaller Applications
Cluster unreachableSync fails, ArgoCD/Flux marks app out-of-syncConfigure retry intervals appropriately
drift detection too sensitiveConstant re-syncing, wasting resourcesConfigure ignoreDifferences in ArgoCD

Sync Failure Recovery Flow


flowchart TD
    A[Sync Triggered] --> B{Manifest Valid?}
    B -->|Invalid YAML| C[Sync Blocked]
    B -->|Valid| D{Resources Healthy?}
    D -->|No| E[Mark Degraded]
    D -->|Yes| F[Sync Complete]
    C --> G[Check Git commit]
    E --> H[Alert Team]
    F --> I[Monitor for Drift]
    G --> H
    H --> J[Fix and Force Sync]

Secret Rotation Without Disruption

GitOps and secrets require care. Never commit plain-text secrets to Git.


# Use sealed-secrets or external secrets operator
# Encrypt secrets before committing
kubectl create secret generic db-creds \
  --from-literal=password=supersecret \
  --dry-run=client \
  -o yaml | kubeseal --cert pub-cert.pem

# Flux external secrets example
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-creds
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: db-creds
  data:
    - secretKey: password
      remoteRef:
        key: prod/db
        property: password

Observability Hooks

Track GitOps health through sync status, drift metrics, and reconciliation times.

What to monitor:

  • Sync status per application (OutOfSync, Synced, Degraded)
  • Time since last successful sync
  • Reconciliation duration (Flux) or sync duration (ArgoCD)
  • Drift count across all managed resources
  • Failed sync attempts and error messages

# ArgoCD - check sync status
argocd app get myapp --watch

# ArgoCD - list out-of-sync apps
argocd app list -o wide | grep OutOfSync

# Flux - check reconciliation status
flux get kustomizations

# Flux - check reconciliation logs
flux logs --kind=Kustomization --name=myapp

# Prometheus metrics from ArgoCD
argocd_app_sync_total{app_name="myapp", phase="success"}
argocd_app_sync_total{app_name="myapp", phase="failure"}
argocd_app_metadata{sync_token="abcd123"} # tracks commit SHA

Common Pitfalls / Anti-Patterns

Committing sensitive data to Git

This is the most common GitOps mistake. Sealed secrets, external secrets, or vault integration are mandatory. There is no acceptable reason to put passwords in Git, even in private repositories.

Not using automated sync

Running ArgoCD or Flux in manual sync mode defeats the purpose. The value of GitOps is the automatic convergence to desired state. Manual sync means you have all the operational overhead of GitOps with none of the self-healing benefits.

Overly broad prune policies

With prune: true, the operator deletes resources removed from Git. On a shared cluster with multiple teams, this can cause incidents if your path patterns are too broad. Test prune behavior in staging before enabling in production.

Ignoring sync wave ordering

When deploying multiple applications that depend on each other, the order matters. ArgoCD and Flux both support dependency ordering, but it is not enabled by default. Without it, your database might try to start before the PVC is created.

Using the same repository for everything

Monorepos with thousands of applications create performance problems. The operator scans the entire repo on every change. Split by team or by deployment boundary to keep sync times reasonable.

Trade-off Analysis

AspectArgoCDFluxJenkins X
UIFull UI dashboardCLI / WebUI onlyWeb UI
Multi-clusterApplicationSets (native)Workload partitioningLimited
Learning curveModerateSteeperSteep
GitOps modelPull-basedPull-basedPull-based
Helm supportYesYesYes
Kustomize supportYesYes (Kustomization)Yes
ExtensibilityPluginsGo modulesPlugins
Enterprise featuresArgo CD EnterpriseWeave GitOps (Enterprise)Limited

Interview Questions

1. What are the four GitOps principles?

Expected answer points:

  • Declarative description: All infrastructure and applications defined declaratively
  • Git as source of truth: Desired state stored in Git, not in running clusters
  • Automated synchronization: Software automatically syncs cluster state to Git state
  • Pull-based updates: Operators pull changes from Git, not pushed by CI
2. How does ArgoCD detect and correct drift?

Expected answer points:

  • ArgoCD continuously monitors Git repositories and compares desired state with actual cluster state
  • When drift is detected, the application is marked OutOfSync
  • With automated syncPolicy, ArgoCD automatically corrects drift by applying the Git state
  • Use argocd app diff to manually inspect differences
  • Use argocd app sync to manually trigger synchronization
3. What is the difference between ArgoCD and Flux architecture?

Expected answer points:

  • ArgoCD: Runs as a Kubernetes controller with built-in web UI and Application resource model
  • Flux: Uses GitRepository and Kustomization CRDs with a reconciler pattern
  • ArgoCD provides a UI for non-technical users; Flux is more CLI-oriented
  • Flux offers more granular control through CRD-based operators
  • Both are pull-based GitOps operators
4. How do you handle secrets in GitOps?

Expected answer points:

  • Never commit plain-text secrets to Git under any circumstances
  • Use sealed-secrets to encrypt secrets before committing
  • Use External Secrets Operator with Vault or other secret managers
  • Flux has native ClusterSecretStore and ExternalSecret resources
  • Sealed-secrets requires a certificate public key; decryption needs the private key on the cluster
5. What is an ArgoCD ApplicationSet and when would you use it?

Expected answer points:

  • ApplicationSet is a controller that generates multiple Application resources from a template
  • Use cases: managing the same application across multiple clusters, multiple teams, or multiple environments
  • Generators: Git directory generator, Cluster generator, Matrix generator, etc.
  • Scales GitOps to hundreds of applications without manual creation
6. How does Flux handle multi-environment deployments with dependencies?

Expected answer points:

  • Flux Kustomization resources support dependsOn for ordering
  • Production Kustomization can depend on staging, waiting for staging to be healthy first
  • DependsOn creates explicit dependency chains between environments
  • This ensures database is ready before application starts, etc.
7. What are the risks of automated prune policies in GitOps?

Expected answer points:

  • With prune: true, the operator deletes resources removed from Git
  • On shared clusters with multiple teams, broad path patterns can accidentally delete other team's resources
  • Always test prune behavior in staging before enabling in production
  • Consider using Application-level prune settings rather than cluster-wide
8. How do you recover from a failed sync in ArgoCD?

Expected answer points:

  • Check if the manifest has invalid YAML - sync gets blocked
  • Verify Git commit is valid and accessible
  • Use argocd app history to review past sync attempts
  • Use argocd app sync --force for a hard reset
  • Check for resource health issues - mark Degraded if resources are unhealthy
  • Alert the team if manual intervention is needed
9. What is sync wave ordering in ArgoCD and why does it matter?

Expected answer points:

  • When deploying multiple applications with dependencies, order matters
  • Database might try to start before the PVC is created
  • ArgoCD and Flux support dependency ordering but it is not enabled by default
  • ArgoCD uses sync waves; Flux uses dependsOn in Kustomization
  • Without proper ordering, dependent services fail to start
10. Why is it bad practice to use the same repository for everything in GitOps?

Expected answer points:

  • Monorepos with thousands of applications create performance problems
  • The operator scans the entire repo on every change
  • Sync times become unreasonable as the repo grows
  • Split by team or by deployment boundary to keep sync times reasonable
  • Consider repository-per-team or repository-per-application patterns
11. How do you monitor GitOps health?

Expected answer points:

  • Track sync status per application: OutOfSync, Synced, Degraded
  • Monitor time since last successful sync
  • Track reconciliation/sync duration
  • Monitor drift count across managed resources
  • Track failed sync attempts and error messages
  • Prometheus metrics available from ArgoCD: argocd_app_sync_total
12. What is the difference between self-heal and automated sync in ArgoCD?

Expected answer points:

  • selfHeal: corrects drift caused by manual changes to the cluster (external modifications)
  • automated: automatically applies changes when Git state differs from cluster state (new commits)
  • Both work together - selfHeal handles drift correction, automated handles deployment updates
  • Disable selfHeal when you need to make temporary manual changes for debugging
13. How does Flux's GitRepository resource work?

Expected answer points:

  • GitRepository defines the source of truth - which Git repo, branch, and path to monitor
  • The spec.interval defines how often Flux checks for new commits
  • secretRef points to credentials for private repos
  • The ignore field allows excluding files from synchronization (e.g., documentation)
  • Kustomization resources reference GitRepository as their source
14. What happens when Git credentials expire in a GitOps setup?

Expected answer points:

  • Operator stops syncing, drift accumulates silently
  • The cluster continues running with outdated configurations
  • No alerts for days until someone notices OutOfSync status
  • Mitigation: Use service accounts with token rotation instead of static credentials
  • Implement credential expiry monitoring and rotation automation
15. How do you implement GitOps for a multi-cluster setup with ArgoCD?

Expected answer points:

  • Use ArgoCD ApplicationSets with cluster generator for scale
  • ApplicationSet template generates Applications for each target cluster
  • Use labels on clusters to filter targets (e.g., environment: production)
  • Directory generator walks a clusters/ path in Git to discover cluster definitions
  • Central Hub Cluster pattern: one ArgoCD instance manages multiple remote clusters
16. What is the role of Kustomize in GitOps workflows?

Expected answer points:

  • Kustomize provides overlay-based configuration management
  • Base configurations + environment overlays (dev, staging, prod)
  • ArgoCD has native Kustomize support in Application spec
  • Flux uses Kustomization CRD which applies Kustomize overlays
  • Common labels and image tags can be patched across environments
17. How do you handle rollback in GitOps?

Expected answer points:

  • Rollback is simply reverting a Git commit
  • Use git revert to create a new commit that undoes the bad change
  • ArgoCD/Flux automatically syncs the revert to the cluster
  • For instant rollback, use git reset --hard to previous good commit
  • argocd app history shows all previous syncs with timestamps
  • argocd app sync --revision HEAD~1 to sync to previous version
18. What is the difference between ArgoCD and Flux in handling Helm charts?

Expected answer points:

  • ArgoCD: Has native Helm support with valueFiles and parameters in Application spec
  • Flux: Uses HelmRelease CRD which is a separate resource type
  • Flux HelmRelease has install and upgrade crds options (Create vs CreateReplace)
  • Both support Helm repos and chart versioning
  • ArgoCD can treat Helm output as manifests; Flux has explicit HelmRelease
19. Why might an ArgoCD application get stuck in progressing state?

Expected answer points:

  • Large manifest causes timeout - split into smaller Applications
  • Invalid YAML in manifests blocks sync
  • Resource dependencies not met (PVC not created before Pod)
  • Health check failing for custom resources
  • Misconfigured ignoreDifferences causing false drift detection
20. How do you secure ArgoCD server access in production?

Expected answer points:

  • Use Ingress with HTTPS and ssl-passthrough for ArgoCD server
  • Configure nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
  • Enable SSO/OIDC integration instead of local users
  • Use dex for external identity provider integration
  • Store credentials in Kubernetes secrets, not in Application manifests
  • Implement RBAC for namespace-level access control

Further Reading

Conclusion

Key Takeaways

  • GitOps makes Git the source of truth for both infrastructure and applications
  • ArgoCD provides a UI and scales well with ApplicationSets
  • Flux uses CRD-based operators for fine-grained control
  • Always use external secrets or sealed secrets, never plain-text credentials
  • Automated sync with self-heal is what separates GitOps from CI-driven deployments
  • Configure ignoreDifferences to prevent alert fatigue from managed fields

GitOps Health Checklist


# Verify ArgoCD sync status
argocd app list

# Check Flux reconciliation
flux get all --namespace flux-system

# View sync history
argocd app history myapp

# Force a clean sync
argocd app sync myapp --force

# Flux: reconcile with source refresh
flux reconcile kustomization myapp --with-source

# Check for drift
argocd app diff myapp

# Verify secrets are not in Git
git log --all --full-history -S "password" -- "*.yaml"

Category

Related Posts

GitOps: Infrastructure as Code with Git for Microservices

Discover GitOps principles and practices for managing microservices infrastructure using Git as the single source of truth.

#microservices #gitops #infrastructure-as-code

Kustomize: Native Kubernetes Configuration Management

Use Kustomize for declarative Kubernetes configuration management without Helm's templating—overlays, patches, and environment-specific customization.

#kubernetes #kustomize #devops

Container Security: Image Scanning and Vulnerability Management

Implement comprehensive container security: from scanning images for vulnerabilities to runtime security monitoring and secrets protection.

#container-security #docker #kubernetes