GitOps with Argo CD
GitOps makes Git the source of truth and lets an operator keep clusters in line with it. This guide covers the principles and the Argo CD concepts and configuration you use day to day.
GitOps principles
GitOps manages infrastructure and applications by treating Git as the single source of truth. The core ideas:
- Declarative: you describe the desired state (YAML, Helm charts, Kustomize overlays) rather than scripting steps.
- Versioned and auditable: every change is a commit or pull request, so history, review and rollback come for free. Rolling back means reverting a commit.
- Automatically applied: an agent applies approved state without humans running
kubectl. - Continuously reconciled: the agent keeps comparing actual state with desired state and corrects drift, such as a manual edit to a Service. This is what makes the system self-healing.
Pull versus push deployment
In a push model a CI pipeline builds, tests and then deploys directly to the cluster. It is simple, but the pipeline holds cluster credentials, scales awkwardly across many clusters, and does nothing if someone changes the cluster by hand afterwards.
In a pull model an operator running in or next to the cluster watches Git and applies changes itself. Cluster credentials never leave the cluster, drift is detected and corrected, and the same repository can drive many clusters.
| Aspect | Push | Pull (GitOps) |
|---|---|---|
| Who applies | CI pipeline | Operator |
| Credentials | Held by CI | Stay in cluster |
| Drift | Not corrected | Detected and healed |
| Rollback | Re-run pipeline | Revert commit |
A common layout uses two repositories: an application repo where CI builds and publishes an image, and a configuration repo holding manifests. CI's last step updates the image tag in the config repo; the GitOps operator does the rest.
Argo CD architecture and concepts
Argo CD is a declarative GitOps continuous-delivery controller for Kubernetes. Its main components:
- API server: serves the web UI, CLI and API, and handles authentication, SSO and RBAC.
- Repository server: clones Git repositories, caches them and renders manifests from plain YAML, Helm or Kustomize.
- Application controller: compares live state with the rendered target state and performs syncs.
Vocabulary worth knowing: target state (what Git says), live state (what the cluster has), sync status (Synced or OutOfSync), sync (the act of moving live toward target), refresh (re-fetch Git and re-compare) and health (Healthy, Progressing, Degraded, Suspended, Missing, Unknown). Health can be extended with custom checks written in Lua.
Applications, projects and app-of-apps
An Application custom resource ties a source (repo, revision, path) to a destination (cluster and namespace):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
spec:
project: default
source:
repoURL: https://git.example.com/org/config.git
targetRevision: main
path: apps/guestbook
destination:
server: https://kubernetes.default.svc
namespace: guestbook
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
An AppProject groups applications and restricts which repositories may be used, which clusters and namespaces may be targeted, and which resource kinds may be deployed. Use projects to separate teams or environments.
The app-of-apps pattern is one Application whose path contains more Application manifests (often generated from a Helm chart). Syncing the parent creates all the children, so a whole platform bootstraps from one resource. ApplicationSet generators serve a similar purpose when you need one app per cluster or per directory.
Sync policies, hooks and health
- Manual sync: Argo CD reports differences but waits for you to press Sync.
- Automated sync: changes in Git are applied without intervention.
- Prune: resources deleted from Git are deleted from the cluster. Off by default for safety.
- Self-heal: manual changes in the cluster are reverted.
- Retry with backoff helps with transient failures.
By default Argo CD re-checks Git on a timer (a few minutes). Configure a Git webhook to trigger refreshes immediately on push.
Resource hooks run Jobs at points in a sync: PreSync (for example a schema migration), Sync, PostSync and SyncFail. Sync waves order resources inside a phase using the argocd.argoproj.io/sync-wave annotation, lower numbers first.
metadata:
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-1"
Kustomize and Helm sources
Kustomize builds environment variants from a shared base plus per-environment overlays of patches, without templating. A kustomization.yaml lists resources, patches, name prefixes, labels and generators.
app/
base/ deployment.yaml service.yaml kustomization.yaml
overlays/
dev/ kustomization.yaml patch.yaml
prod/ kustomization.yaml patch.yaml
kustomize build overlays/dev
kubectl apply -k overlays/dev
Point an Application's path at an overlay and Argo CD runs the build for you. For Helm, set source.helm.valueFiles or inline values, or reference a chart from a Helm or OCI registry. Argo CD only uses Helm to render templates; it then manages the resources itself, so helm ls will not list them.
Deployment strategies
- Recreate: stop old pods, start new ones. Simple, with downtime.
- Rolling update: replace pods gradually, tuned with
maxSurgeandmaxUnavailable. The Kubernetes default. - Blue/green: run the new version beside the old and switch traffic once verified. Fast rollback, double the capacity.
- Canary: send a small share of traffic to the new version and increase it as metrics stay healthy.
Blue/green and canary need extra machinery. Argo Rollouts provides a Rollout resource (a Deployment replacement) with pause and promote steps, traffic shaping through ingress or service mesh, and analysis-driven automatic promotion or rollback. An existing Deployment can be adopted with workloadRef, then scaled down once the Rollout is serving.
RBAC and SSO
Argo CD ships with two built-in roles: role:readonly and role:admin. Custom policies live in the argocd-rbac-cm ConfigMap as CSV lines:
p, role:dev-team, applications, sync, my-project/*, allow
p, role:dev-team, applications, get, my-project/*, allow
g, my-idp-group, role:dev-team
Policies name a subject, resource (applications, projects, repositories, clusters, logs, exec and others), action (get, create, update, delete, sync, override) and object. Application objects are addressed as project/application. Map identity-provider groups to roles with g lines and set policy.default to a minimal role.
For SSO, Argo CD can use an OIDC provider directly or its bundled Dex component, which can federate SAML and LDAP providers. The pattern is: register an application at the identity provider, expose a group claim, set the public Argo CD URL in argocd-cm, and add the connector settings there. Keep client secrets in Kubernetes Secrets, not in the ConfigMap.
Handling secrets in Git
Plain Kubernetes Secrets are only base64-encoded, so never commit them. Two common approaches:
- Sealed Secrets: a controller in the cluster holds a private key. You encrypt a Secret locally with the public certificate using
kubesealand commit the resultingSealedSecret. Only that cluster can decrypt it. - External Secrets: Git holds only a reference; an operator fetches the value from an external secret manager (such as a cloud provider's secret service or HashiCorp Vault) and creates the Kubernetes Secret at runtime.
kubectl create secret generic app-creds \
--from-literal=password=changeme --dry-run=client -o yaml \
| kubeseal --cert pub-cert.pem --format yaml > sealed-app-creds.yaml
Back up the sealing key and plan for rotation. If the controller's private key is lost, every sealed secret must be re-created.