Introduction to GitOps: ArgoCD and Flux
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:
- Declarative: Desired state defined in Git
- Versioned: Full history of all changes
- Automated: Agents reconcile actual vs desired state
- 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:
- Watch the Git repository
- Apply changes when commits land
- Self-heal if someone manually changes resources
- Prune resources removed from Git
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
| Feature | ArgoCD | Flux |
|---|---|---|
| UI | Rich web UI | CLI-focused |
| Architecture | Monolithic | Modular toolkit |
| Helm support | Built-in | Via controllers |
| Image automation | Limited | First-class |
| Multi-tenancy | Application projects | Namespaces |
| Learning curve | Lower | Higher |
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
- Separate app and infra repos: Different change frequencies
- Use environments: staging → production promotion
- Require PR reviews: Treat manifests like code
- Test in CI: Validate YAML before merge
- Monitor sync status: Alert on failures
- 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.