AI EngineeringZero to ProductionHome·About·Contact
Kubernetes Orchestration · Part 6

Helm & GitOps (Argo CD)

Hand-applying YAML doesn't scale. This lesson is how teams actually manage manifests: Helm templates one chart into many environments with a values.yaml, and GitOps (via Argo CD) makes git the single source of truth — a controller continuously syncs the cluster to what's in the repo and flags drift. This closes the loop back to the CI/CD you built in CD5 and the ch08 DevOps capstone: your pipeline writes to git, and the cluster converges on its own.

⏱️ ~90 min☸️ Packaging + delivery🎯 Intermediate→Professional
🌱 From `kubectl apply` to a pipelineTwo tools, one idea: stop applying YAML by hand. Helm is templating/packaging; Argo CD is continuous delivery from git. Commands need a cluster (Helm) and Argo CD installed in it; output is illustrative. Helm and Argo CD are separate projects that version independently — verify chart syntax, CLI flags, and Argo CRD fields against their current docs.

Learning objectives

  • Explain why raw YAML doesn't scale across environments, and how Helm's templating fixes it.
  • Read and write a Helm chart: templates, values.yaml, releases.
  • Install/upgrade/rollback a release with the helm CLI.
  • Explain GitOps: git as source of truth, pull-based sync, drift detection.
  • Define an Argo CD Application and reason about sync, self-heal, and drift.

1 · Why raw YAML doesn't scale

You have the same app in dev, staging, and prod — same shape, different replica counts, image tags, and hostnames. Copy-pasting three near-identical manifest sets means every change is three edits and a chance to drift. You need one templated definition plus per-environment values. That's Helm.

Helm in one lineHelm is a templating and packaging tool: a chart is a parameterized bundle of manifests, a values file supplies the parameters, and a release is one installed instance of a chart in a cluster. Same chart + different values = dev, staging, prod from one source.

2 · A Helm chart, templated

A chart is a directory: Chart.yaml (metadata), a values.yaml (defaults), and templates/ (manifests with {{ }} placeholders). Helm renders the templates against the values to produce plain Kubernetes YAML.

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
YAML · a templated Deployment in templates/deployment.yaml (needs Helm)
templates/deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-web
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels: { app: {{ .Release.Name }}-web }
  template:
    metadata:
      labels: { app: {{ .Release.Name }}-web }
    spec:
      containers:
        - name: app
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.containerPort }}
YAML · values.yaml (defaults) + a prod override (needs Helm)
values.yaml# values.yaml  (defaults)
replicaCount: 2
image:
  repository: ghcr.io/example/web
  tag: "1.2.0"
containerPort: 8000
---
# values-prod.yaml  (overrides only what differs)
replicaCount: 6
image:
  tag: "1.2.0"        # promote a tested tag to prod
Templates + values = one source, many environmentsThe template is written once. helm install with the default values gives you dev; adding -f values-prod.yaml gives you prod from the same chart. No copy-paste, no drift between environments' base shape.

3 · The helm lifecycle

Helm manages a release like a package manager: install it, upgrade it, and roll it back — with history.

shell · install, upgrade, and roll back a release (needs a cluster)
helm.shhelm install web ./web-chart                       # release named "web"
helm install web ./web-chart -f values-prod.yaml   # prod values
helm upgrade web ./web-chart --set image.tag=1.3.0 # change one value
helm history web                                   # revisions
helm rollback web 1                                # back to revision 1
helm uninstall web
REVISION  UPDATED    STATUS      CHART        APP VERSION
1         ...        superseded  web-0.1.0    1.2.0
2         ...        deployed    web-0.1.0    1.3.0
`--set` on the CLI is fine for demos, dangerous for prodAd-hoc --set image.tag=1.3.0 changes state that isn't recorded in git — the classic way environments drift from their definition. In production you want the values in a repo and applied by a pipeline, not typed at a terminal. That's exactly the gap GitOps closes.

4 · GitOps — git as the source of truth

GitOps flips the delivery model. Instead of a pipeline pushing kubectl apply into the cluster, a controller inside the cluster pulls the desired state from a git repo and continuously makes the cluster match it. The repo is the single source of truth; every change is a git commit (reviewed, audited, revertable); and the cluster self-heals toward git.

Commit to git repo Argo CD watches repo Syncs cluster to match Cluster = git
Push CI/CD (CD5)GitOps (pull)
Who appliesPipeline runs kubectl applyIn-cluster controller pulls from git
Source of truthWhatever last ranThe git repo, always
Cluster credentialsHeld by the CI systemStay inside the cluster
DriftSilent until next runDetected continuously; can auto-revert
Audit / rollbackPipeline logsgit log / git revert

5 · Argo CD Applications, sync & drift

Argo CD is the leading GitOps controller. You define an Application that points at a repo path and a target cluster/namespace. Argo CD compares the rendered manifests in git to what's live and reports Synced or OutOfSync; it can auto-sync, and with self-heal it reverts manual kubectl changes back to git.

YAML · an Argo CD Application with auto-sync + self-heal (verify CRD fields vs Argo docs)
application.yamlapiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/example/deploy.git
    targetRevision: main
    path: web-chart
    helm:
      valueFiles: [values-prod.yaml]
  destination:
    server: https://kubernetes.default.svc
    namespace: web
  syncPolicy:
    automated:
      prune: true        # delete resources removed from git
      selfHeal: true     # revert manual drift back to git
With self-heal on, the cluster is not editable by handOnce selfHeal: true is set, a kubectl edit or kubectl scale you run by hand is drift — Argo CD will revert it to match git within moments. That's the feature, not a bug: the only way to change the cluster is to change git. Teams learning GitOps are surprised the first time their manual fix vanishes. Verify current Argo CD Application CRD fields against its docs, which evolve.

6 · Closing the loop with CI (CD5 → here → ch08)

Put it together and you get the full delivery pipeline this course has been building toward: CI (CD5) builds and tests the image and pushes it to a registry, then commits the new tag to the deploy repo; Argo CD sees the commit and syncs the cluster. Your ch08 DevOps agent can even be the thing that opens that PR. CI's job ends at git; delivery is the cluster pulling from git.

The clean separation GitOps gives youCI produces artifacts and proposes desired state (a git commit); CD (GitOps) makes reality match desired state. The pipeline never holds cluster credentials, every deploy is a reviewed commit, and rollback is git revert. This is the delivery half of the ch08 capstone's story.

✓ Checkpoint — you can move on when you can…

  • Explain why raw per-environment YAML drifts, and how a chart + values files fixes it.
  • Write a templated Helm Deployment and a prod values override.
  • Install, upgrade, and roll back a Helm release from the CLI.
  • Explain GitOps (git as truth, pull-based sync, drift detection) vs push CI/CD.
  • Define an Argo CD Application and say what auto-sync + self-heal do.
✓ Knowledge check

Under Argo CD with selfHeal: true, an on-call engineer runs kubectl scale deploy/web --replicas=10 to handle a spike. Minutes later replicas are back to 6. Why, and what should they have done?

Show answer
Argo CD treats the live cluster as needing to match git. The manual scale is drift from the committed desired state (6 replicas), and with self-heal enabled Argo CD reverts it back to git within its sync interval. To make the change stick, edit the desired state in the repo — bump replicaCount (or the HPA bounds) in values-prod.yaml, commit, and let Argo CD sync it. Under GitOps the cluster is a projection of git; you change git, not the cluster. (For genuine emergencies teams keep a documented break-glass procedure.)
✓ Knowledge check

A team uses Helm but deploys to prod with helm upgrade --set image.tag=... from engineers' laptops. What are the two main risks, and how does GitOps address them?

Show answer
Risk 1: the deployed state isn't recorded anywhere reviewable — the tag lives only in someone's shell history, so no one can see or audit what's actually running, and environments drift. Risk 2: every engineer's laptop holds cluster credentials, a broad security surface. GitOps fixes both: the values (including the image tag) live in a git repo changed only by reviewed commits, so the running state is always the repo's HEAD and fully auditable; and an in-cluster controller (Argo CD) pulls from git, so no external system or laptop needs cluster credentials. Rollback becomes git revert.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Template a Deployment with HelmBeginner

Context: Templating is the first step off copy-pasted YAML; a parameterized Deployment is the canonical starter chart.

Your task: Turn a hardcoded Deployment into a Helm template driven by a values file for replica count, image, and port.

Requirements:

  • Parameterize replicas, image repo+tag, and container port
  • Provide sensible defaults in values.yaml
  • Show the install command

💡 Hint: Reference values with {{ .Values.x }} and the release name with {{ .Release.Name }}.

Show solution

templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata: { name: {{ .Release.Name }}-web }
spec:
  replicas: {{ .Values.replicaCount }}
  selector: { matchLabels: { app: {{ .Release.Name }}-web } }
  template:
    metadata: { labels: { app: {{ .Release.Name }}-web } }
    spec:
      containers:
        - name: app
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports: [ { containerPort: {{ .Values.containerPort }} } ]

values.yaml:

replicaCount: 2
image: { repository: ghcr.io/example/web, tag: "1.2.0" }
containerPort: 8000

Install it: helm install web ./web-chart. Helm renders the template against the values into plain Kubernetes YAML and applies it as the release web.

Exercise 2 · One chart, two environmentsIntermediate

Context: The payoff of Helm is running the same chart in dev and prod with only a values diff.

Your task: Show how to deploy the same chart to dev and prod, overriding only what differs, and give the two install commands.

Requirements:

  • Keep defaults for dev in values.yaml
  • Put only the differences in values-prod.yaml
  • Give both install/upgrade commands
  • State why this beats maintaining two manifest copies

💡 Hint: Override files should contain only the deltas, not a full copy.

Show solution

values-prod.yaml contains only deltas:

replicaCount: 6        # dev default was 2
image: { tag: "1.2.0" } # the promoted, tested tag
helm install web ./web-chart                       # dev (defaults)
helm install web-prod ./web-chart -f values-prod.yaml   # prod (overrides)

Why it beats two copies: the base shape (probes, labels, ports, strategy) is defined once in the chart. A change to that shape is one edit that both environments inherit, and the only thing that can differ is what's explicitly in an override file — so environments can't silently drift apart in structure. Two hand-maintained manifest sets inevitably diverge.

Exercise 3 · Install, upgrade, and roll back a releaseAdvanced

Context: Treating a deployment as a versioned release with history is what makes upgrades and rollbacks safe.

Your task: Walk through installing a release, upgrading its image, inspecting history, and rolling back — and note the one habit that keeps this clean.

Requirements:

  • Install, then upgrade the image tag
  • Show helm history and interpret the revisions
  • Roll back to a known-good revision
  • State why --set from a laptop is a bad prod habit

💡 Hint: Each helm operation is a numbered revision you can return to.

Show solution
helm install web ./web-chart
helm upgrade web ./web-chart --set image.tag=1.3.0
helm history web         # revision 1 superseded, revision 2 deployed
helm rollback web 1      # instantly return to revision 1

Interpreting history: each install/upgrade is a numbered, immutable revision; helm rollback web 1 re-applies revision 1's rendered manifests, so recovery from a bad upgrade is one command with no rebuild.

The clean habit: in production, don't drive --set from a laptop — that state lives nowhere reviewable and drifts. Keep values in git and let a pipeline / Argo CD apply them, so helm history and git log tell the same story.

Exercise 4 · Convert a push deploy to a GitOps ApplicationExpert

Context: Moving from pipeline-push to pull-based GitOps is the delivery upgrade most teams make; you should be able to define the Application and explain the shift.

Your task: Write an Argo CD Application that deploys a Helm chart from git with auto-sync and self-heal, and explain how this changes the deploy workflow.

Requirements:

  • Point the Application at a repo path and values file
  • Enable prune + selfHeal and explain each
  • Describe how a deploy now happens (what the engineer does)
  • Note the credential/security benefit vs push CI/CD

💡 Hint: Under GitOps the engineer commits; the controller deploys.

Show solution
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata: { name: web, namespace: argocd }
spec:
  project: default
  source:
    repoURL: https://github.com/example/deploy.git
    targetRevision: main
    path: web-chart
    helm: { valueFiles: [values-prod.yaml] }
  destination: { server: https://kubernetes.default.svc, namespace: web }
  syncPolicy: { automated: { prune: true, selfHeal: true } }

prune deletes cluster resources that were removed from git (git is authoritative); selfHeal reverts any manual drift back to git.

New workflow: to deploy, an engineer opens a PR that bumps the image tag / values in the deploy repo; on merge, Argo CD sees the commit and syncs the cluster. Nobody runs kubectl apply.

Security benefit: the CI system no longer needs cluster credentials — the in-cluster Argo CD pulls from git, shrinking the attack surface and making every deploy a reviewed, auditable commit. Verify the Application CRD fields against current Argo CD docs.

Exercise 5 · Design the CI→GitOps pipeline (closing CD5 → ch08)Professional

Context: The professional artifact is the whole delivery pipeline: CI builds and proposes, GitOps delivers — with a clean credential and rollback story.

Your task: Design the end-to-end pipeline from a code push to a running change, splitting responsibilities cleanly between CI and GitOps.

Requirements:

  • Define what CI does and where its job ends
  • Define what GitOps does and its source of truth
  • Explain the promotion path dev → staging → prod
  • State the rollback story and the credential boundary

💡 Hint: CI's job ends at a git commit; GitOps takes it from there.

Show solution

CI (extends CD5): on a code push, build the image, run tests, scan it, push to the registry (ECR/GHCR) with an immutable tag, then commit that tag into the deploy repo (e.g. bump image.tag in values-staging.yaml). CI's responsibility ends at the commit — it never touches the cluster.

GitOps (this lesson): Argo CD watches the deploy repo (source of truth) and syncs each environment's Application to its values file. The cluster continuously converges on git and self-heals drift.

Promotion path: the same chart flows dev → staging → prod as promotions between values files/branches — a merge that copies the tested tag from values-staging.yaml to values-prod.yaml. Each promotion is a reviewed PR.

Rollback & credentials: rollback is git revert of the promotion commit — Argo CD syncs back automatically. The credential boundary is clean: CI holds registry and git write access but not cluster credentials; only the in-cluster controller can change the cluster. This is the delivery backbone of the ch08 DevOps capstone.

Exercise 6 · Package and deliver an app via Helm + Argo CD end to endIndustry scenario

Context: Representative scenario: you're asked to make a service's deployment reproducible and self-healing across three environments, replacing ad-hoc kubectl/helm-from-laptop.

Your task: Produce the full setup: a chart, per-environment values, Argo CD Applications for each environment, and the workflow the team follows to ship and roll back.

Requirements:

  • Chart with parameterized image, replicas, resources, probes
  • values-dev / values-staging / values-prod overrides
  • An Argo CD Application per environment with appropriate sync policy
  • The ship + rollback runbook the team uses
  • State what stops working the old way (and why that's good)

💡 Hint: Consider a looser sync policy in dev, strict self-heal in prod.

Show solution

1 · One chart parameterizes image repo/tag, replica count, resources, and probes in values.yaml — the base shape all environments share.

2 · Three values files hold only deltas: values-dev.yaml (1 replica, latest built tag, auto-sync no self-heal for fast iteration), values-staging.yaml (prod-like, self-heal on), values-prod.yaml (higher replicas/HPA bounds, self-heal on, manual sync window if desired).

3 · An Argo CD Application per environment points at the chart path with the matching values file and target namespace/cluster. Prod uses automated: { prune: true, selfHeal: true }; dev may relax self-heal so engineers can experiment.

4 · Ship runbook: CI builds+tests+pushes the image and commits the new tag to values-dev.yaml; Argo CD deploys dev. Promotion to staging then prod is a reviewed PR copying the tested tag forward. Nobody runs kubectl apply or helm --set against prod.

5 · Rollback runbook: git revert the offending promotion commit; Argo CD syncs the previous state back automatically (and helm rollback remains a break-glass fallback).

What stops working — deliberately: hand-editing prod with kubectl or helm --set no longer sticks (self-heal reverts it). That's the goal: the only path to change prod is a reviewed git commit, so the running state is always exactly what's in the repo. Verify Helm and Argo CD syntax against their current docs.

© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in