Introduction
ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes. GitOps means your Git repository is the single source of truth for your infrastructure and application state — ArgoCD continuously watches your Git repo and ensures your Kubernetes cluster matches what's defined there. If someone makes a manual change to the cluster, ArgoCD detects the drift and can automatically revert it. This guide teaches you to implement GitOps with ArgoCD.
Why GitOps with ArgoCD?
Traditional CI/CD: Code → Build → Push → CI system PUSHES to cluster
GitOps: Code → Build → Update Git → ArgoCD PULLS from Git to cluster
Benefits:
- Audit trail: Every deployment is a Git commit (who, what, when)
- Easy rollback:
git revert= instant rollback - Drift detection: Cluster always matches desired state
- No cluster credentials in CI: CI doesn't need kubectl access
Installing ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Wait for all pods to be ready
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s
# Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
# Port-forward to access UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Access at https://localhost:8080 (admin / <password above>)Install ArgoCD CLI:
curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd && sudo mv argocd /usr/local/bin/
# Login
argocd login localhost:8080 --username admin --password <password> --insecureExposing ArgoCD via Ingress
# argocd-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
rules:
- host: argocd.company.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 443Creating Your First Application
# app-myapp.yaml - Define ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-production
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io # Clean up when app deleted
spec:
project: default
source:
repoURL: https://github.com/company/k8s-manifests.git
targetRevision: main # Branch/tag/commit to watch
path: apps/myapp/production # Directory within repo
destination:
server: https://kubernetes.default.svc # Target cluster
namespace: production
syncPolicy:
automated:
prune: true # Delete resources removed from Git
selfHeal: true # Revert manual changes
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
retry:
limit: 5
backoff:
duration: 5s
maxDuration: 3m
factor: 2kubectl apply -f app-myapp.yaml
# Check status
argocd app list
argocd app get myapp-production
# Manual sync (if automated sync disabled)
argocd app sync myapp-production
# Wait for sync to complete
argocd app wait myapp-production --syncRepository Structure for GitOps
k8s-manifests/
├── apps/
│ ├── myapp/
│ │ ├── base/ # Kustomize base
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ └── kustomization.yaml
│ │ ├── staging/
│ │ │ ├── kustomization.yaml # Patches for staging
│ │ │ └── values-patch.yaml
│ │ └── production/
│ │ ├── kustomization.yaml # Patches for production
│ │ └── replicas-patch.yaml
│ └── database/
│ └── ...
├── infrastructure/
│ ├── ingress-nginx/
│ └── cert-manager/
└── argocd/
└── applications/ # ArgoCD Application manifestsKustomization example:
# apps/myapp/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: replicas-patch.yaml
images:
- name: company/myapp
newTag: v3.5.2 # Update this for deploymentsApp of Apps Pattern
Manage multiple applications with a single ArgoCD Application:
# argocd/app-of-apps.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: app-of-apps
namespace: argocd
spec:
source:
repoURL: https://github.com/company/k8s-manifests.git
targetRevision: main
path: argocd/applications # Directory with all Application CRDs
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: trueCI/CD Integration
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
update-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: company/k8s-manifests
token: ${{ secrets.GITOPS_TOKEN }}
- name: Update image tag
run: |
# Update kustomization.yaml with new image tag
cd apps/myapp/production
kustomize edit set image company/myapp=company/myapp:${{ github.sha }}
- name: Commit and push
run: |
git config user.email "ci@company.com"
git config user.name "CI Bot"
git add .
git commit -m "Update myapp to ${{ github.sha }}"
git pushRBAC and Projects
# argocd-project.yaml - Limit what each team can deploy
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: team-backend
namespace: argocd
spec:
description: Backend team deployments
sourceRepos:
- 'https://github.com/company/k8s-manifests.git'
destinations:
- namespace: backend-staging
server: https://kubernetes.default.svc
- namespace: backend-production
server: https://kubernetes.default.svc
clusterResourceWhitelist: [] # No cluster-level resources
namespaceResourceWhitelist:
- group: 'apps'
kind: 'Deployment'
- group: ''
kind: 'Service'Monitoring ArgoCD
# Watch all applications
argocd app list -w
# Check specific app diff (what would change on next sync)
argocd app diff myapp-production
# View sync history
argocd app history myapp-production
# Rollback to specific revision
argocd app rollback myapp-production 42ArgoCD transforms Kubernetes deployments into a Git-centric workflow. Once implemented, every deployment becomes auditable, every rollback becomes a git revert, and your cluster state becomes verifiable against your repository.
