· CI/CD  · 3 min read

GitOps on Kubernetes with ArgoCD

ArgoCD changed how I think about deployments. Here's how to set up GitOps for your Kubernetes workloads — and why you won't go back to manual kubectl applies.

ArgoCD changed how I think about deployments. Here's how to set up GitOps for your Kubernetes workloads — and why you won't go back to manual kubectl applies.

What Is GitOps?

GitOps is a deployment model where your Git repository is the single source of truth for infrastructure and application state. Instead of running kubectl apply manually or triggering imperative scripts, a GitOps operator continuously reconciles your cluster state with what’s declared in Git.

The key properties:

  • Declarative — desired state is defined in Git
  • Automated — changes in Git automatically sync to the cluster
  • Observable — drift between Git and cluster is detected and reported
  • Auditable — every change has a Git commit, author, and timestamp

Why ArgoCD?

I’ve used ArgoCD across multiple production EKS environments. It’s become my default for Kubernetes deployments because it:

  • Provides a clean UI to visualize application state
  • Detects and alerts on configuration drift
  • Supports Helm, Kustomize, and plain YAML
  • Integrates cleanly with GitHub Actions pipelines
  • Handles multi-cluster and multi-tenant setups

Installation

kubectl create namespace argocd

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

# Install ArgoCD CLI
brew install argocd

# Port-forward to access the UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

For production, use Helm and expose via your Ingress:

helm repo add argo https://argoproj.github.io/argo-helm

helm install argocd argo/argo-cd \
  --namespace argocd \
  --create-namespace \
  --set server.ingress.enabled=true \
  --set server.ingress.hosts[0]=argocd.internal.yourdomain.com

Your First Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default

  source:
    repoURL: https://github.com/your-org/your-helm-charts
    targetRevision: HEAD
    path: charts/my-app
    helm:
      values: |
        image:
          tag: latest
        replicaCount: 2

  destination:
    server: https://kubernetes.default.svc
    namespace: production

  syncPolicy:
    automated:
      prune: true      # Delete resources removed from Git
      selfHeal: true   # Re-sync if cluster drifts from Git
    syncOptions:
      - CreateNamespace=true

Apply it:

kubectl apply -f my-app.yaml

ArgoCD will detect the application and start syncing immediately.

The CI/CD + GitOps Flow

My standard pipeline pattern with GitHub Actions:

Code push → GitHub Actions (build + test + push image) → Update Helm values in Git → ArgoCD detects change → Syncs to cluster

The GitHub Actions step that updates the image tag:

- name: Update image tag in Helm values
  run: |
    git clone https://github.com/your-org/your-helm-charts.git
    cd your-helm-charts

    # Update the image tag
    sed -i "s/tag: .*/tag: ${{ github.sha }}/" charts/my-app/values.yaml

    git config user.email "ci@jakops.dev"
    git config user.name "GitHub Actions"
    git add .
    git commit -m "ci: deploy my-app@${{ github.sha }}"
    git push

ArgoCD polls Git every 3 minutes by default, or you can use webhooks for instant sync.

App of Apps Pattern

For managing many applications, use the App of Apps pattern — one ArgoCD application that manages other ArgoCD applications:

# root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root
  namespace: argocd
spec:
  source:
    repoURL: https://github.com/your-org/gitops-repo
    path: apps/
    targetRevision: HEAD
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
gitops-repo/
  apps/
    my-app.yaml
    another-service.yaml
    monitoring.yaml
    ingress-nginx.yaml

Each file in apps/ is an ArgoCD Application manifest. Add a new file, ArgoCD picks it up automatically. Remove a file, ArgoCD prunes the application.

Rollbacks

One of the best features: rollbacks are a Git revert away.

# Via CLI
argocd app rollback my-app <revision-id>

# Or just revert the Git commit — ArgoCD will sync automatically
git revert HEAD
git push

The cluster state follows Git. Always.

Conclusion

ArgoCD + GitOps fundamentally changes your relationship with deployments. Instead of worrying about what’s running in the cluster, you trust Git. Drift is detected automatically, rollbacks are instant, and every change is auditable. Once you operate this way, going back to imperative deployments feels reckless.

Back to Blog

Related Posts

View All Posts »