Skip to content
SRE & DevOpsHow-To Published Updated 5 min readViews unavailable

How to Implement GitOps with ArgoCD

Implement GitOps with Argo CD using pinned installation assets, constrained projects, safe automated sync, drift detection, and tested recovery.

GitOps treats a Git repository as the authoritative source of truth for what should be running in a cluster — instead of applying changes with kubectl or helm directly, you commit changes to Git and let a controller reconcile the cluster to match. Argo CD is one of the most widely used tools implementing this pattern.

Step 1: install Argo CD into the cluster

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

Replace VERSION with a reviewed release tag compatible with the cluster. The stable branch is convenient for a lab but mutable; production changes should be reproducible. Review the release notes and manifest diff, then record the selected version in infrastructure code. For a highly available control plane, follow the official HA installation guidance rather than assuming the default manifest is sufficient.

Step 2: access the Argo CD UI

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

Open https://localhost:8080 — the initial admin password is auto-generated and retrievable from a Kubernetes secret:

kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath="{.data.password}" | base64 -d

Use the initial account only for bootstrap. Configure SSO, RBAC, TLS, and audited access; rotate or remove the initial secret and disable the built-in admin account once recovery access is proven. Exposing the server through an ingress before authentication and certificates are correctly configured creates an avoidable control-plane risk.

Step 3: prepare a Git repository describing your desired cluster state

myapp-gitops-repo/
  manifests/
    deployment.yaml
    service.yaml

This can be plain Kubernetes YAML, a Helm chart, or Kustomize overlays — ArgoCD supports all three as sources of truth.

Step 4: constrain the application with an AppProject

An Application can otherwise point at broad repositories, clusters, and namespaces allowed by the Argo CD installation. Define an AppProject that allowlists the intended source repository and destination, denies cluster-scoped resources unless required, and maps roles to the correct teams. This is a security boundary, not merely a folder for organizing applications.

Step 5: define an Argo CD Application pointing at that repository

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
spec:
  source:
    repoURL: https://github.com/you/myapp-gitops-repo
    path: manifests
    targetRevision: main
  destination:
    server: https://kubernetes.default.svc
    namespace: myapp
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

automated.selfHeal: true makes Argo CD reconcile eligible drift from Git. prune: true also deletes resources no longer present in the desired state, so enable it only after reviewing what the Application owns. A bad commit can be reconciled just as efficiently as a good one. Protect the repository, review generated manifests, and consider manual sync for high-risk namespaces.

Step 6: apply the Application resource

kubectl apply -f myapp-application.yaml

Step 7: watch Argo CD sync the cluster to match Git

Argo CD UI → Applications → myapp → observe sync status

The UI shows a diff between live and desired resources and separate sync and health states. Synced does not necessarily mean the application works: health checks, hooks, tests, and external monitoring must verify the customer path. Configure ignore-differences rules narrowly; broad exclusions can hide real drift.

Step 8: deploy a change by committing to Git, not by running kubectl

# edit manifests/deployment.yaml, changing the image tag
git commit -am "bump myapp to v1.1.0"
git push

Argo CD detects the new commit and applies it according to the configured sync policy. Pin container images by digest in the desired state; a mutable tag can change without a Git commit and defeats the audit trail. Use sync waves or hooks only when ordering is truly required, and make every hook idempotent because retries and partial failures happen.

Step 9: roll back by reverting the Git commit

git revert HEAD
git push

Reverting restores declarative Kubernetes resources, but does not undo database writes, external API calls, queues, or destructive migrations. Use backward-compatible migrations and a documented data-recovery procedure. Verify the revert commit actually became the observed revision and run the same health checks used for promotion.

Back up and test the GitOps control plane

Git stores desired state, not every Argo CD setting or credential. Keep declarative Projects, Applications, RBAC, repository configuration, and notifications in protected configuration where possible. Back up required secrets through an approved secrets system and test recovery into a clean cluster. Monitor reconciliation latency, repository errors, expired credentials, orphaned resources, and repeated sync failures.

Avoid giving engineers routine direct production write access while claiming Git is authoritative. Preserve a break-glass path for incidents, make it time-limited and audited, and require any emergency live change to be represented or reverted in Git immediately afterward.

Why “Git as source of truth” is a meaningfully different model, not just automation

Traditional CI/CD pipelines push changes to a cluster by running commands against it; GitOps inverts this — a controller running inside the cluster continuously pulls from Git and reconciles differences. This means every change to production has an inherent audit trail (Git history), rollback is a Git operation rather than a separate manual procedure, and drift — someone making an unreviewed manual change directly against the cluster — gets automatically detected and corrected rather than silently persisting until someone happens to notice.

Related:

Sources:

Comments