Skip to main content
โšก Calmops

GitOps vs Infrastructure as Code: Understanding the Differences

GitOps and Infrastructure as Code (IaC) are both fundamental practices in modern DevOps, but they serve different purposes and can be used together. Understanding when to use each approach is key to building reliable deployment pipelines.

In this guide, we’ll explore GitOps, IaC, their differences, and how to combine them effectively.

Understanding Infrastructure as Code

What is IaC?

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Infrastructure as Code (IaC)                    โ”‚
โ”‚                                                             โ”‚
โ”‚   Instead of:          We Write:                            โ”‚
โ”‚                                                             โ”‚
โ”‚   Click in console    Terraform/CloudFormation/Pulumi      โ”‚
โ”‚   Manual config       Code that creates resources           โ”‚
โ”‚   Procedural steps   Declarative desired state              โ”‚
โ”‚                                                             โ”‚
โ”‚   Example:                                               โ”‚
โ”‚                                                             โ”‚
โ”‚   # terraform/main.tf                                      โ”‚
โ”‚   resource "aws_instance" "web" {                         โ”‚
โ”‚     ami           = "ami-12345"                           โ”‚
โ”‚     instance_type = "t3.micro"                            โ”‚
โ”‚     tags = {                                               โ”‚
โ”‚       Name = "web-server"                                  โ”‚
โ”‚     }                                                      โ”‚
โ”‚   }                                                        โ”‚
โ”‚                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

IaC Tools

# Popular IaC tools

tools = {
    "terraform": {
        "type": "Declarative",
        "provider": "Multi-cloud",
        "state": "State file",
        "use_when": "Multi-cloud, complex infrastructure"
    },
    
    "cloudformation": {
        "type": "Declarative",
        "provider": "AWS only",
        "state": "Managed by AWS",
        "use_when": "AWS-only environments"
    },
    
    "pulumi": {
        "type": "Declarative (code)",
        "provider": "Multi-cloud",
        "state": "State file",
        "use_when": "Want real programming language"
    },
    
    "ansible": {
        "type": "Procedural",
        "provider": "Configuration management",
        "state": "Stateless",
        "use_when": "Server configuration, provisioning"
    }
}

Terraform Example

# Complete Terraform setup

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# VPC
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = {
    Name = "main-vpc"
  }
}

# Subnets
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block             = "10.0.1.0/24"
  map_public_ip_on_launch = true
  
  tags = {
    Name = "public-subnet"
  }
}

# Security Group
resource "aws_security_group" "web" {
  name        = "web-sg"
  description = "Security group for web servers"
  vpc_id      = aws_vpc.main.id
  
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# EC2 Instance
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
  subnet_id     = aws_subnet.public.id
  
  vpc_security_group_ids = [aws_security_group.web.id]
  
  tags = {
    Name = "web-server"
    Environment = "production"
  }
}

Understanding GitOps

What is GitOps?

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      GitOps Concept                           โ”‚
โ”‚                                                             โ”‚
โ”‚   Git is the single source of truth:                        โ”‚
โ”‚                                                             โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”‚
โ”‚   โ”‚   Git    โ”‚โ”€โ”€โ”€โ–บโ”‚  CI/CD   โ”‚โ”€โ”€โ”€โ–บโ”‚ Cluster  โ”‚            โ”‚
โ”‚   โ”‚ Repositoryโ”‚    โ”‚ Pipeline โ”‚    โ”‚          โ”‚            โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ”‚
โ”‚        โ”‚                                    โ”‚               โ”‚
โ”‚        โ”‚  Changes flow through Git           โ”‚               โ”‚
โ”‚        โ”‚  (Pull requests, reviews, merges)  โ”‚               โ”‚
โ”‚        โ”‚                                    โ”‚               โ”‚
โ”‚        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ”‚
โ”‚                                                             โ”‚
โ”‚   Key principles:                                            โ”‚
โ”‚   1. Declarative infrastructure                             โ”‚
โ”‚   2. Git as source of truth                                 โ”‚
โ”‚   3. Automated sync                                         โ”‚
โ”‚   4. Drift detection                                        โ”‚
โ”‚                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

GitOps Tools

# GitOps tools

tools = {
    "argocd": {
        "description": "GitOps for Kubernetes",
        "type": "Pull-based",
        "features": ["Multi-tenancy", "UI", "Rollbacks"]
    },
    
    "flux": {
        "description": "GitOps for Kubernetes",
        "type": "Pull-based",
        "features": ["Lightweight", "Extensible"]
    },
    
    "jenkins_x": {
        "description": "Cloud-native CI/CD with GitOps",
        "type": "Integrated",
        "features": ["Preview envs", "GitOps native"]
    },
    
    "argoproj_rollouts": {
        "description": "Progressive delivery for GitOps",
        "features": ["Blue-green", "Canary", "Analysis"]
    }
}

ArgoCD Example

# ArgoCD Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
spec:
  project: default
  
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: deploy/k8s
    
  destination:
    server: https://kubernetes.default.svc
    namespace: production
    
  syncPolicy:
    automated:
      prune: true        # Remove old resources
      selfHeal: true    # Fix drift
      allowEmpty: false
    
    syncOptions:
    - CreateNamespace=true
    
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

---

# Application with multiple sources

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  sources:
  - repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: k8s/overlays/production
  
  - repoURL: https://github.com/myorg/infrastructure
    targetRevision: main
    path: helm-charts/mychart
    helm:
      valueFiles:
      - values-prod.yaml

Key Differences

Comparison Table

Aspect IaC GitOps
Focus Provisioning infrastructure Deploying applications
Trigger Push (CI/CD pipeline) Pull (GitOps operator)
Target Cloud resources (VPC, RDS) Kubernetes resources
State State file (tfstate) Git repository
Drift Manual check Auto-sync
Tools Terraform, CloudFormation ArgoCD, Flux
When Initial setup, infra changes Day-2 operations

Use Cases

# When to use IaC

iac_use_cases = [
    "Create VPCs and networking",
    "Provision databases (RDS, DynamoDB)",
    "Set up load balancers",
    "Configure IAM roles and policies",
    "Initial cluster setup",
    "One-time infrastructure"
]

# When to use GitOps

gitops_use_cases = [
    "Deploy applications to Kubernetes",
    "Manage Kubernetes manifests",
    "Manage Helm charts",
    "Manage configMaps and secrets",
    "Continuous deployment",
    "Day-2 operations"
]

Combining IaC and GitOps

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           Combined IaC + GitOps Architecture                 โ”‚
โ”‚                                                             โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚   โ”‚                  Git Repository                      โ”‚ โ”‚
โ”‚   โ”‚                                                     โ”‚ โ”‚
โ”‚   โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚ โ”‚
โ”‚   โ”‚  โ”‚   infra/    โ”‚    โ”‚     apps/                 โ”‚   โ”‚ โ”‚
โ”‚   โ”‚  โ”‚  (Terraform)โ”‚    โ”‚     (Kubernetes manifests)โ”‚   โ”‚ โ”‚
โ”‚   โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚ โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚          โ”‚                           โ”‚                     โ”‚
โ”‚          โ–ผ                           โ–ผ                     โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚   โ”‚  Terraform   โ”‚          โ”‚   ArgoCD     โ”‚           โ”‚
โ”‚   โ”‚   Pipeline   โ”‚          โ”‚   Operator   โ”‚           โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚          โ”‚                           โ”‚                     โ”‚
โ”‚          โ–ผ                           โ–ผ                     โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚   โ”‚ Cloud Providerโ”‚          โ”‚  Kubernetes  โ”‚           โ”‚
โ”‚   โ”‚ (AWS/GCP/etc)โ”‚          โ”‚   Cluster   โ”‚           โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                             โ”‚
โ”‚   Terraform creates the cluster                            โ”‚
โ”‚   ArgoCD deploys apps to it                               โ”‚
โ”‚                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Implementation

# 1. Terraform creates the cluster

# terraform/eks.tf
resource "aws_eks_cluster" "main" {
  name     = "my-cluster"
  role_arn = aws_iam_role.eks_cluster.arn
  version  = "1.28"
  
  vpc_config {
    subnet_ids = aws_subnet.private[*].id
  }
}

# 2. ArgoCD operator watches for deployments

# apps/myapp.yaml (committed to Git)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: k8s
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Best Practices

GitOps Best Practices

# GitOps best practices

gitops_practices = [
    "Use separate repos for infra and apps",
    "Enable branch protection",
    "Require PR reviews for changes",
    "Use meaningful commit messages",
    "Enable auto-sync with drift detection",
    "Set up notifications for sync failures",
    "Use Helm or Kustomize for templating"
]

# Directory structure
structure = """
โ”œโ”€โ”€ infrastructure/          # Terraform
โ”‚   โ”œโ”€โ”€ modules/
โ”‚   โ”œโ”€โ”€ environments/
โ”‚   โ”‚   โ”œโ”€โ”€ dev/
โ”‚   โ”‚   โ””โ”€โ”€ prod/
โ”‚   โ””โ”€โ”€ main.tf
โ”‚
โ”œโ”€โ”€ apps/                   # Kubernetes
โ”‚   โ”œโ”€โ”€ myapp/
โ”‚   โ”‚   โ”œโ”€โ”€ base/
โ”‚   โ”‚   โ”œโ”€โ”€ overlays/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dev/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ prod/
โ”‚   โ”‚   โ””โ”€โ”€ kustomization.yaml
โ”‚   โ””โ”€โ”€ otherapp/
"""

IaC Best Practices

# IaC best practices

iac_practices = [
    "Use modules for reusability",
    "Never commit secrets to Git",
    "Use workspaces for environments",
    "Enable state locking",
    "Use remote state (S3/GCS)",
    "Enable drift detection",
    "Version your modules"
]

# Secret handling
secret_handling = """
# Use:
# - HashiCorp Vault
# - AWS Secrets Manager
# - Azure Key Vault
# - SOPS with age/yaml

# terraform/secrets.tf
data "aws_secrets_manager_secret_version" "db_creds" {
  name = "myapp/database"
}

# DON'T:
# - Commit secrets to Git
# - Store in tfstate
# - Hardcode in code
"""

Migration Path

# Moving to GitOps

migration_steps = [
    {
        "step": "1. Start with IaC",
        "tasks": [
            "Define infrastructure in Terraform",
            "Set up remote state",
            "Enable state locking"
        ]
    },
    
    {
        "step": "2. Containerize apps",
        "tasks": [
            "Dockerize applications",
            "Set up image registry",
            "Add image scanning to CI"
        ]
    },
    
    {
        "step": "3. Add Kubernetes",
        "tasks": [
            "Provision cluster with Terraform",
            "Create K8s manifests",
            "Set up Helm charts"
        ]
    },
    
    {
        "step": "4. Implement GitOps",
        "tasks": [
            "Install ArgoCD/Flux",
            "Create Application CRDs",
            "Enable auto-sync",
            "Set up PR workflows"
        ]
    },
    
    {
        "step": "5. Automate everything",
        "tasks": [
            "Add drift detection",
            "Set up notifications",
            "Enable progressive delivery"
        ]
    }
]

Conclusion

Both IaC and GitOps are essential:

  • IaC: Provisioning cloud infrastructure (Terraform)
  • GitOps: Deploying applications to Kubernetes (ArgoCD)

Use them together: IaC creates the cluster, GitOps manages what’s deployed to it.


Comments