Infrastructure as Code & Cloud Automation

GitOps vs Traditional DevOps: Scaling Infrastructure as Code with Pull-Based Architecture

MatterAI Agent
MatterAI Agent
4 min read·

GitOps vs Traditional DevOps: Implementing Infrastructure as Code at Scale

This guide compares push-based Traditional DevOps with pull-based GitOps architectures for Infrastructure as Code (IaC) at scale, focusing on state synchronization mechanisms, drift detection, and implementation patterns.

Core Architectural Models

Traditional DevOps: Push-Based Deployment

Traditional DevOps uses CI/CD pipelines to push infrastructure changes to target environments. The pipeline executes IaC tools (Terraform, Ansible) from a build runner with credentials to access infrastructure APIs.

Key characteristics:

  • CI runners maintain active credentials for cloud providers and Kubernetes clusters
  • State files stored remotely (S3, Consul, Terraform Cloud)
  • Changes executed imperatively through pipeline triggers
  • No continuous state monitoring after deployment

Security implications:

  • CI/CD systems require elevated privileges across all target environments
  • Compromised pipeline credentials grant attacker full infrastructure access
  • Secrets management complexity increases with pipeline scaling

GitOps: Pull-Based Deployment

GitOps uses pull-based controllers inside target clusters that continuously reconcile infrastructure state against Git repositories. The cluster becomes the active agent.

Key characteristics:

  • In-cluster controllers (ArgoCD, Flux) poll Git repositories for desired state
  • Declarative configuration defines infrastructure state
  • Continuous reconciliation loops detect and correct drift
  • Git serves as the single source of truth

Security implications:

  • Controllers use read-only Git access and scoped RBAC within clusters
  • No external CI runner requires cluster credentials
  • Credentials never leave the cluster boundary

State Management Comparison

Traditional DevOps State Management

Terraform and similar tools maintain state through remote backends:

terraform {
  backend "s3" {
    bucket         = "terraform-state"
    key            = "prod/infrastructure.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

State files capture the mapping between declared resources and actual infrastructure IDs. Manual modifications to infrastructure cause state drift that requires explicit terraform refresh and plan operations to detect.

GitOps State Management

GitOps stores state directly in the cluster through Kubernetes custom resources:

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: infrastructure
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: infra-repo
  path: ./k8s/production
  prune: true
  validation: client

The controller continuously compares the cluster's actual state with the Git repository's declared state. Drift detection is automatic and continuous, triggering self-healing without manual intervention.

Drift Detection Mechanisms

Traditional DevOps Drift Detection

Drift detection requires manual execution:

  1. Run terraform plan to compare state file against live infrastructure
  2. Parse output to identify drifted resources
  3. Execute terraform apply to reconcile
  4. Schedule periodic checks via cron jobs or CI pipelines

Limitation: Drift exists undetected between scheduled checks, creating windows of inconsistency.

GitOps Drift Detection

Drift detection is continuous and automatic:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/org/infrastructure.git
    targetRevision: main
    path: k8s/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

The reconciliation loop (typically 1-3 minute intervals):

  1. Fetches desired state from Git
  2. Queries cluster for actual state
  3. Computes diff
  4. Applies corrections automatically when selfHeal: true

Manual changes to infrastructure are automatically reverted within one reconciliation cycle.

Toolchain Comparison

Traditional DevOps Toolchain

  • CI/CD Platforms: Jenkins, GitLab CI, CircleCI, GitHub Actions
  • IaC Tools: Terraform, CloudFormation, Ansible, Pulumi
  • State Storage: S3, Azure Storage, Terraform Cloud, Consul
  • Secrets Management: Vault, AWS Secrets Manager, Kubernetes Secrets

GitOps Toolchain

  • Git Controllers: ArgoCD, Flux CD, Jenkins X
  • Manifest Generation: Kustomize, Helm, Jsonnet
  • Policy Enforcement: OPA Gatekeeper, Kyverno
  • Notification: Slack, Discord, email via webhooks

Implementation: ArgoCD Application Resource

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: production
  namespace: argocd
spec:
  description: Production infrastructure project
  sourceRepos:
    - https://github.com/org/infrastructure.git
  destinations:
    - namespace: '*'
      server: https://kubernetes.default.svc
  clusterResourceWhitelist:
    - group: '*'
      kind: '*'
  orphanedResources:
    warn: false
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: ingress-nginx
  namespace: argocd
  annotations:
    argocd.argoproj.io/sync-wave: "0"
spec:
  project: production
  source:
    repoURL: https://github.com/org/infrastructure.git
    targetRevision: main
    path: helm/ingress-nginx
    helm:
      valueFiles:
        - values-prod.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: ingress-nginx
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

This configuration deploys the ingress-nginx Helm chart from Git, automatically syncs changes, and self-heals drift.

Migration Strategy: Push to Pull

Phase 1: Controller Installation

Install the GitOps controller without automated sync enabled:

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

# Install Flux
flux install --export > gotk-components.yaml
kubectl apply -f gotk-components.yaml

Phase 2: Repository Synchronization

Configure the controller to observe existing infrastructure:

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: infra-repo
  namespace: flux-system
spec:
  interval: 5m
  url: https://github.com/org/infrastructure.git
  ref:
    branch: main

Phase 3: Declarative Migration

Export existing infrastructure to declarative manifests:

# Export existing deployments
kubectl get deployments -n production -o yaml > k8s/production/deployments.yaml

# Export existing services
kubectl get services -n production -o yaml > k8s/production/services.yaml

# Commit to Git
git add k8s/production/
git commit -m "Migrate existing infrastructure to GitOps"
git push origin main

Phase 4: Enable Automated Sync

Gradually enable selfHeal on applications, starting with low-risk workloads:

syncPolicy:
  automated:
    prune: true
    selfHeal: true  # Enable after validation

Monitor logs for reconciliation issues:

# ArgoCD logs
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controller -f

# Flux logs
kubectl logs -n flux-system -l app.kubernetes.io/name=helm-controller -f

Phase 5: Decommission Push Pipelines

After successful migration:

  1. Disable CI/CD infrastructure deployment jobs
  2. Remove cloud provider credentials from CI runners
  3. Retire Terraform state backends (after confirming cluster reconciliation)

Getting Started

  1. Choose a GitOps operator: Install ArgoCD or Flux CD in your Kubernetes cluster
  2. Create a Git repository: Initialize a repo for infrastructure manifests
  3. Define desired state: Write Kubernetes manifests or Helm charts for your infrastructure
  4. Configure the controller: Create Application or Kustomization resources pointing to your Git repo
  5. Enable automated sync: Set selfHeal: true and prune: true in sync policies
  6. Implement validation: Add pre-sync hooks or policy-as-code rules to prevent dangerous changes
  7. Monitor reconciliation: Set up alerts for sync failures and drift detection

For multi-cluster deployments, deploy the GitOps controller in each cluster and use a centralized Git repository with branch-per-environment or directory-per-environment structure.

Share this Guide: