Introduction to GitOps: ArgoCD and Flux

devops kubernetes gitops

GitOps is the natural evolution of infrastructure as code. Instead of imperatively running commands to change infrastructure, you declare the desired state in Git, and automation makes it so.

What is GitOps?

Core principles:

  1. Declarative: Desired state defined in Git
  2. Versioned: Full history of all changes
  3. Automated: Agents reconcile actual vs desired state
  4. Observable: Detect and alert on drift

The workflow:

Developer → Git commit → Automation → Kubernetes cluster

No manual kubectl apply. No SSH to production. Git is the only interface.

Why GitOps?

Audit trail: Every change is a commit with author, message, timestamp.

Rollback: git revert is your rollback mechanism.

Security: No cluster credentials on developer machines.

Consistency: Automation ensures declared state matches actual state.

Developer experience: Familiar Git workflows for infrastructure.

ArgoCD: The Kubernetes GitOps Operator

ArgoCD continuously syncs Git repos to Kubernetes clusters.

Installation

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

# Access UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

Creating an Application

# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/my-app-manifests
    targetRevision: HEAD
    path: kubernetes/
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

ArgoCD will:

Sync Strategies

Manual sync: Approve each deployment

syncPolicy: {}  # No automated sync

Automated with safeguards:

syncPolicy:
  automated:
    prune: true       # Delete resources removed from Git
    selfHeal: true    # Revert manual changes
    allowEmpty: false # Don't sync empty directories

Multi-Environment Setup

manifests/
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
├── overlays/
│   ├── staging/
│   │   └── kustomization.yaml
│   └── production/
│       └── kustomization.yaml

One Application per environment:

# staging
source:
  path: manifests/overlays/staging

# production  
source:
  path: manifests/overlays/production

Flux: The CNCF GitOps Toolkit

Flux is modular and composable, ideal for complex setups.

Installation

flux bootstrap github \
  --owner=myorg \
  --repository=fleet-infra \
  --path=clusters/production \
  --personal

GitRepository and Kustomization

# Source
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
  name: my-app
  namespace: flux-system
spec:
  interval: 1m
  url: https://github.com/myorg/my-app
  ref:
    branch: main

# Reconciliation
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
  name: my-app
  namespace: flux-system
spec:
  interval: 5m
  path: ./kubernetes
  sourceRef:
    kind: GitRepository
    name: my-app
  prune: true

Image Automation

Flux can automatically update image tags:

apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
  name: my-app
spec:
  imageRepositoryRef:
    name: my-app
  policy:
    semver:
      range: ">=1.0.0"
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: my-app
spec:
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        name: fluxcdbot
  update:
    path: ./kubernetes
    strategy: Setters

ArgoCD vs Flux

FeatureArgoCDFlux
UIRich web UICLI-focused
ArchitectureMonolithicModular toolkit
Helm supportBuilt-inVia controllers
Image automationLimitedFirst-class
Multi-tenancyApplication projectsNamespaces
Learning curveLowerHigher

Choose ArgoCD if: You want a polished UI and simpler setup.

Choose Flux if: You need image automation or highly customizable workflows.

Repository Strategies

Monorepo

infrastructure/
├── apps/
│   ├── app-a/
│   └── app-b/
├── clusters/
│   ├── staging/
│   └── production/
└── base/

Simpler but can get large.

Polyrepo

app-a-manifests/
app-b-manifests/
cluster-configs/

More repos, clearer ownership, more complexity.

Secrets Management

Secrets in Git? Not directly. Options:

Sealed Secrets

kubeseal --format yaml < secret.yaml > sealedsecret.yaml
# Commit sealedsecret.yaml
# Controller decrypts in-cluster

SOPS + Age/AWS KMS

sops --encrypt secret.yaml > secret.enc.yaml
# Commit encrypted file
# Flux/ArgoCD decrypts during reconciliation

External Secrets

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-secret
spec:
  secretStoreRef:
    name: aws-parameter-store
  target:
    name: my-secret
  data:
    - secretKey: password
      remoteRef:
        key: /prod/myapp/password

Best Practices

  1. Separate app and infra repos: Different change frequencies
  2. Use environments: staging → production promotion
  3. Require PR reviews: Treat manifests like code
  4. Test in CI: Validate YAML before merge
  5. Monitor sync status: Alert on failures
  6. Document rollback: Ensure team knows the process

Final Thoughts

GitOps brings software engineering practices to infrastructure. Every change is reviewed, tested, and auditable.

Start with a simple app on ArgoCD. Experience the workflow. Expand to more applications and eventually infrastructure components.


Git is your new kubectl.

All posts