Implement GitOps with ArgoCD on Kubernetes

Deployments feel shaky when you have to click through a UI, edit a YAML file on the fly, and pray the pod starts. That nervous feeling is why many teams are moving to GitOps – a way to let Git be the single source of truth for everything that runs in the cluster. In this post I’ll walk you through a practical workflow using ArgoCD, the open‑source tool that makes GitOps feel almost magical on Kubernetes.

Why GitOps matters right now

If you’ve ever rolled back a release because a config typo broke the service, you know the pain of “manual drift”. When you store the desired state in Git, every change is versioned, reviewed, and can be replayed. That alone cuts down on hot‑fixes and makes audits a breeze. Plus, with the cloud moving faster than ever, you need a repeatable, automated path from code to cluster – GitOps gives you exactly that.

Core concepts in plain language

  • Git as source of truth – Think of Git as the master recipe book. Anything you want running in the cluster lives in a repo.
  • Declarative state – Instead of telling Kubernetes “run this command now”, you describe what you want (pods, services, config) and let the system make it happen.
  • Continuous reconciliation – ArgoCD constantly checks the live cluster against the repo. If they differ, it can automatically fix the drift or alert you.

Setting up the basics

1. Prepare a Git repo

Create a new repo under your favorite Git host (GitHub, GitLab, Bitbucket). Inside, make a folder called manifests/ and drop in the Kubernetes YAML files for your app – deployment, service, ingress, configmap, everything. Keep the structure simple; for example:

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

I like to use Kustomize for overlays because it lets me keep a clean base and add environment‑specific tweaks without copying files.

2. Install ArgoCD on the cluster

Run the following one‑liner (you need kubectl already pointing at your cluster):

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

That pulls the ArgoCD control plane into the argocd namespace. Wait a minute for the pods to start, then expose the UI with port‑forward:

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

Now you can open https://localhost:8080 in your browser. The default admin password is the pod name of argocd-server; you can fetch it with:

kubectl -n argocd get pod -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f2

3. Connect ArgoCD to your repo

In the UI, click Settings → Repositories and add a new Git repo. Supply the HTTPS URL and a personal access token with read rights. If you prefer CLI, the same can be done with:

argocd repo add https://github.com/yourorg/yourrepo.git \
  --username youruser --password $TOKEN

Defining an application in ArgoCD

An Application is ArgoCD’s way of saying “this part of the repo should be synced to this namespace”. Create a file called argocd-app.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/yourorg/yourrepo.git
    targetRevision: HEAD
    path: manifests/overlays/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true          # delete resources that are removed from Git
      selfHeal: true       # fix drift automatically
    syncOptions:
      - CreateNamespace=true

Apply it:

kubectl apply -f argocd-app.yaml

ArgoCD will now read the manifests, compare them to what’s running in production, and create any missing resources. The UI will show a green check when everything is in sync.

A practical workflow for reliable deployments

  1. Feature branch – Developers work on a feature branch, add or modify manifests as needed, and push to the remote.
  2. Pull request – The PR triggers CI (GitHub Actions, GitLab CI, etc.) that runs kubeval or kube-score to catch syntax errors early.
  3. Review & merge – Once the PR is approved, it merges into main. The main branch is the GitOps “live” branch.
  4. ArgoCD auto‑sync – Because we set automated.selfHeal to true, ArgoCD sees the new commit and starts a sync. If the sync fails, ArgoCD marks the app as “OutOfSync” and you can view the error details.
  5. Rollback – Need to roll back? Just revert the commit in Git, and ArgoCD will automatically roll the cluster back to the previous state. No extra scripts, no manual kubectl delete.

Handling secrets safely

Never store raw secrets in the repo. I use Sealed Secrets – you encrypt a secret locally, commit the sealed version, and let a controller in the cluster decrypt it at runtime. The workflow looks like:

kubectl create secret generic db-pass --from-literal=password=SuperSecret123 -o yaml --dry-run=client | \
  kubeseal --cert mycert.pem -o yaml > sealed-db-pass.yaml
git add sealed-db-pass.yaml
git commit -m "Add sealed DB password"

ArgoCD treats the sealed secret like any other manifest, so the secret ends up in the right namespace without ever exposing the clear text.

Monitoring and troubleshooting

ArgoCD ships with a built‑in dashboard that shows sync status, health, and recent events. If a pod crashes after a sync, the UI will flag the app as “Degraded”. Click the app, go to Events, and you’ll see the exact Kubernetes error. Because the desired state lives in Git, you can always compare the live manifest (kubectl get deployment my-app -o yaml) with the version in the repo to spot differences.

For deeper logs, run:

kubectl logs -n argocd deployment/argocd-repo-server

That shows any repo fetch errors, which are often caused by expired tokens.

Tips to keep your GitOps pipeline smooth

  • Keep manifests small – Split large apps into multiple ArgoCD Applications. This isolates failures and speeds up sync.
  • Use Kustomize or Helm – Both let you templatize values without hard‑coding them.
  • Enable pruning – Without prune: true, removed resources linger in the cluster, leading to hidden drift.
  • Lock down access – Give developers read‑only access to ArgoCD UI; let the CI pipeline handle the actual sync triggers. This reduces accidental manual changes.

My personal takeaway

When I first tried to set up GitOps, I spent a whole afternoon wrestling with a mismatched namespace and a missing kustomization.yaml. The moment I got the automated sync working, the relief was palpable – no more “it works on my laptop” surprises. Now I treat every change as a commit to the repo, and the cluster behaves like a well‑kept garden: you plant a seed, step back, and let the system take care of the growth.

Give ArgoCD a spin on your next project. The learning curve is short, the payoff is big, and your future self will thank you every time a rollback is just a Git revert away.

Reactions