Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
Why an Argo CD Tutorial Still Matters on Kubernetes 1.37
Argo CD is an open-source GitOps continuous delivery tool that watches a Git repository and keeps a Kubernetes cluster matching what it finds there, as the official Argo CD documentation explains. That single idea explains why platform teams adopted it. Git becomes the audit log, the review process and the rollback button for every change that reaches a cluster.
This Argo CD tutorial takes you through 13 steps in roughly 55 minutes. You will build a local Kubernetes cluster, install Argo CD, deploy an app from Git, scale it to several environments with an ApplicationSet, and finish with RBAC and alerts. Still weighing tools before you commit an hour? Our Argo CD vs Flux comparison covers the trade-offs in depth, and this guide assumes you already picked Argo CD.
Timing matters right now. As of October 29, 2026, the Kubernetes project supports three minor branches: 1.37, 1.36 and 1.35. Kubernetes 1.34 stopped receiving upstream patches on October 27, 2026, according to a DEV Community write-up, so any tutorial that still builds on 1.34 or older ships a cluster that nobody patches. On the Argo CD side, the newest stable release listed on the project’s GitHub releases page was v3.5.6, published October 20, 2026. A 3.6.0 release candidate (rc4) appeared two days later, on October 22. It is a prerelease, so keep it out of production.
Argo CD’s roadmap is also moving. CNCF coverage from July 2026 described the 3.5 release as an effort to make ApplicationSets first-class citizens, and a September 30, 2026 CNCF post said the Argo community had begun visioning work for Argo CD 4.0. None of that changes the workflow below. It does mean you should pin versions, read release notes before upgrades, and avoid tutorials that point at a moving stable tag.
Why bother with GitOps at all? In an interview on the Red Hat Technically Speaking podcast page, Chris Wright, identified there as Red Hat’s chief technology officer, put it this way: “GitOps is a set of practices to manage infrastructure and application configurations using Git as a single source of truth.” He added that the core principles require the desired state to be “expressed declaratively and that desired state is versioned and immutable, pulled automatically, and is continuously reconciled.” Argo CD implements exactly that loop.
By the end of the tutorial you will have these pieces running:
- A local kind cluster that mirrors a real Kubernetes 1.37 or 1.36 environment
- Argo CD installed from pinned manifests with a rotated admin password
- A Git repository holding Kustomize manifests for a small web app
- An
AppProjectthat limits what Argo CD may deploy and where - An
ApplicationSetthat creates one Application per environment folder - Sync waves, a PostSync smoke test, a CI account with narrow RBAC and Slack alerts
Everything runs on a laptop with Docker, so the cost is $0 unless you move it to a managed cluster. A later section shows the AWS price math for that case.
Prerequisites and Versions You Need Before Starting
Install the tools below before you open a terminal for Step 1. The table lists what each one does and how strict the version requirement is. Where the table says “latest stable,” run the tool’s own version command and confirm it prints a recent release rather than something from 2024.
| Tool | Version to use | Why you need it |
|---|---|---|
| Docker Engine or Docker Desktop | Current stable release | kind runs each Kubernetes node as a container |
| kind | Latest stable release | Creates the local cluster in about 40 seconds |
| Kubernetes | 1.37 preferred, 1.36 or 1.35 also supported upstream | The cluster Argo CD manages |
| kubectl | Within one minor version of the cluster | Talks to the API server and applies manifests |
| argocd CLI | Same minor version as the server you install | Logs in, syncs apps, manages tokens |
| Git | Any 2.x release | Holds the desired state |
| GitHub account | Free plan is enough | Hosts the demo repository |
| Slack workspace (optional) | Any plan that allows a bot token | Receives sync and health alerts in Step 13 |
Two cautions apply. First, the kind node image decides your Kubernetes version. If you want 1.37 exactly, check kind’s release notes for the node image built for that version and pass it with --image. The default image may lag behind the upstream release. Second, keep kubectl within one minor version of the cluster, because the supported skew is one minor version in either direction.
You also need about 4 GB of free RAM for Docker and a terminal that can run background processes. On Windows, run every command inside WSL 2 with an Ubuntu distribution, since the shell snippets use Bash syntax. Budget 55 minutes if you type the commands yourself and 25 minutes if you copy them from the full project section near the end.
How Argo CD Works: Components and the Sync Loop
Argo CD runs entirely inside your cluster as a small group of controllers. Knowing what each piece does makes the troubleshooting section later in this guide far easier to follow.
- argocd-server exposes the web UI, the API and the gRPC endpoint that the CLI uses.
- argocd-repo-server clones Git repositories and renders manifests from Helm, Kustomize or plain YAML.
- argocd-application-controller compares the rendered manifests with live cluster state and applies changes.
- argocd-applicationset-controller generates Application objects from templates and generators.
- argocd-notifications-controller sends messages to Slack, email, webhooks and other services.
- argocd-redis caches state, and argocd-dex-server handles single sign-on connectors.
Three custom resources carry the configuration. An Application ties one Git path to one destination. An AppProject sets the guardrails, such as which repositories and namespaces an app may touch. An ApplicationSet stamps out many Applications from a generator, for example one per directory or one per cluster.
Argo CD tracks two separate statuses for every Application. Sync status says whether live state matches Git: Synced or OutOfSync. Health status says whether the resources actually work: Healthy, Progressing, Degraded or Missing. A Deployment can be perfectly Synced and still Degraded because the image will not pull. Keep that distinction in mind, because it is the root of half the confusion beginners report.
Drift detection is the feature that makes the pull model valuable. As Akuity’s explanation of Argo CD sync and drift detection describes, the controller keeps comparing live objects against the desired state, so a manual kubectl edit shows up as OutOfSync within moments. With selfHeal enabled, Argo CD reverts the change on its own. By default the repo server also polls Git about every three minutes, and you can cut that delay to seconds with a webhook, which Step 8 and the advanced tips cover.
Phase 1: Build the Cluster and the Git Repository (Steps 1-3)
Step 1: Install and verify the tooling
Install the command-line tools. On macOS or Linux with Homebrew, one command handles everything. On other systems, use each project’s install page, then confirm the versions.
# macOS or Linux with Homebrew
brew install kind kubectl argocd git
# Verify each tool answers
kind version
kubectl version --client
argocd version --client
docker info --format '{{.ServerVersion}}'
If docker info fails, start Docker Desktop or the Docker daemon first. kind cannot create nodes without it.
Step 2: Create the kind cluster
Create a single-node cluster named gitops. One node is enough for this tutorial, and it keeps memory use near 2 GB.
kind create cluster --name gitops
kubectl cluster-info --context kind-gitops
kubectl get nodes
A healthy result looks like the output below. The version column shows whichever Kubernetes release your kind node image carries, so it may read 1.37 or a neighboring minor version.
NAME STATUS ROLES AGE VERSION
gitops-control-plane Ready control-plane 42s v1.37.x
If you do not have a cluster of your own yet and want a longer walkthrough of how clusters are put together, see our guide to building a Kubernetes cluster in 13 steps.
Step 3: Create the Git repository with Kustomize manifests
Create a public GitHub repository named gitops-demo. A public repo needs no credentials, which keeps the first run simple. Step 6 shows how to register a private one. Then build this layout locally:
gitops-demo/
apps/web/base/
configmap.yaml
deployment.yaml
service.yaml
smoke-test.yaml
kustomization.yaml
apps/web/overlays/dev/kustomization.yaml
apps/web/overlays/staging/kustomization.yaml
argocd/
project.yaml
applicationset.yaml
Start with the base manifests. The ConfigMap, Deployment and Service describe a tiny nginx app. The ConfigMap and Service carry sync-wave annotations that Step 11 explains, so add them now and save a later commit.
# apps/web/base/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
annotations:
argocd.argoproj.io/sync-wave: "-1"
data:
MESSAGE: "hello from git"
# apps/web/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: web
template:
metadata:
labels:
app.kubernetes.io/name: web
spec:
containers:
- name: nginx
image: nginx:stable-alpine
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: web-config
readinessProbe:
httpGet:
path: /
port: 80
resources:
requests:
cpu: 25m
memory: 32Mi
limits:
memory: 64Mi
# apps/web/base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: web
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
selector:
app.kubernetes.io/name: web
ports:
- port: 80
targetPort: 80
Next, the PostSync smoke test. Argo CD runs this Job after every successful sync and deletes it when it passes, so a failed check surfaces immediately in the UI.
# apps/web/base/smoke-test.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: web-smoke-test
annotations:
argocd.argoproj.io/hook: PostSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
backoffLimit: 0
template:
spec:
restartPolicy: Never
containers:
- name: check
image: busybox:stable
command: ["sh", "-c", "wget -q -O /dev/null http://web:80 && echo smoke test passed"]
Finish with the kustomizations. The base lists its resources, and each overlay sets a namespace and a replica count.
# apps/web/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- configmap.yaml
- deployment.yaml
- service.yaml
- smoke-test.yaml
# apps/web/overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: web-dev
resources:
- ../../base
patches:
- target:
kind: Deployment
name: web
patch: |-
- op: replace
path: /spec/replicas
value: 1
Copy that file to apps/web/overlays/staging/kustomization.yaml, change the namespace to web-staging and set the replica value to 2. Then render locally to catch typos before Argo CD ever sees them, and push.
kubectl kustomize apps/web/overlays/dev | head -20
git init -b main
git add .
git commit -m "Add web app base and overlays"
git remote add origin https://github.com/YOUR_USER/gitops-demo.git
git push -u origin main
Replace YOUR_USER with your GitHub username everywhere it appears in this tutorial. The local render step is worth keeping as a habit. A manifest that fails kubectl kustomize will also fail inside the repo server, but the error there is harder to read.
Phase 2: Install Argo CD and Log In (Steps 4-6)
Step 4: Install Argo CD from pinned manifests
Pin the version. The install manifests live in the Argo CD repository under each release tag, so a variable keeps every later command consistent. At the time of writing the newest stable tag on the project’s releases page was v3.5.6, but check that page yourself and use whatever stable tag is current when you read this. Skip release candidates.
export ARGOCD_VERSION=v3.5.6
kubectl create namespace argocd
kubectl apply -n argocd --server-side --force-conflicts \
-f https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/manifests/install.yaml
kubectl -n argocd rollout status deploy/argocd-server --timeout=180s
kubectl -n argocd get pods
The --server-side flag is not optional. Some Argo CD custom resource definitions are large enough that a client-side apply fails with an annotation size error. Server-side apply sidesteps the problem, and --force-conflicts lets you re-run the same command during upgrades.
Within a minute or two every pod should report Running. Your pod name suffixes will differ from the sample below.
NAME READY STATUS RESTARTS AGE
argocd-application-controller-0 1/1 Running 0 78s
argocd-applicationset-controller-6b7d9c8f5d-x2k4q 1/1 Running 0 78s
argocd-dex-server-5c8d7b9f6-m9wzt 1/1 Running 0 78s
argocd-notifications-controller-7f4d6c9b8-h6s2p 1/1 Running 0 78s
argocd-redis-64b8c7d5f9-q8v7n 1/1 Running 0 78s
argocd-repo-server-68c9f7b5d4-t5r8j 1/1 Running 0 78s
argocd-server-7d6b9c5f84-k3n6w 1/1 Running 0 78s
Step 5: Log in and replace the admin password
Argo CD generates a random admin password on first install. Expose the API server with a port-forward, read the password with the CLI, and log in. The --insecure flag is acceptable here because the certificate is self-signed and the traffic never leaves your laptop.
kubectl -n argocd port-forward svc/argocd-server 8443:443 > /dev/null 2>&1 &
argocd admin initial-password -n argocd
argocd login localhost:8443 --username admin --insecure
argocd account update-password
kubectl -n argocd delete secret argocd-initial-admin-secret
Open https://localhost:8443 in a browser, accept the certificate warning and sign in as admin with your new password. The empty Applications screen is what you want to see. Deleting the initial secret afterward is good hygiene, because it holds the original password in plain text.
Never leave the port-forward as your long-term access method. For a shared cluster, put the server behind an Ingress or a Gateway API route with a real certificate, then restrict who can reach it. Our guide to deploying applications with Kubernetes and Helm shows a packaging approach that pairs well with this step when you move beyond a laptop.
Step 6: Create an AppProject and register the repository
The built-in default project allows any source and any destination. That is fine for a five-minute demo and dangerous everywhere else. Create a dedicated project that allows only your repository, only namespaces starting with web-, and only the Namespace kind at cluster scope.
# argocd/project.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: demo
namespace: argocd
spec:
description: Demo apps for the GitOps tutorial
sourceRepos:
- https://github.com/YOUR_USER/gitops-demo.git
destinations:
- server: https://kubernetes.default.svc
namespace: web-*
clusterResourceWhitelist:
- group: ""
kind: Namespace
kubectl apply -f argocd/project.yaml
argocd proj get demo
If your repository is private, register credentials now. Use a fine-grained personal access token with read-only access to repository contents, and store it in an environment variable instead of typing it into your shell history.
argocd repo add https://github.com/YOUR_USER/gitops-demo.git \
--username YOUR_USER \
--password "$GITHUB_TOKEN"
argocd repo list
Phase 3: Deploy Your First Application (Steps 7-9)
Step 7: Create the web-dev Application declaratively
You can create Applications from the UI or the CLI, but a YAML manifest is reviewable and repeatable. This one points at the dev overlay, enables automated sync with pruning and self-healing, and asks Argo CD to create the target namespace.
# bootstrap/web-dev.yaml (temporary, replaced in Step 10)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: web-dev
namespace: argocd
spec:
project: demo
source:
repoURL: https://github.com/YOUR_USER/gitops-demo.git
targetRevision: HEAD
path: apps/web/overlays/dev
destination:
server: https://kubernetes.default.svc
namespace: web-dev
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
kubectl apply -f bootstrap/web-dev.yaml
argocd app get web-dev
kubectl -n web-dev get all
The manifest deliberately omits the resources-finalizer.argocd.argoproj.io finalizer. Without it, deleting the Application leaves the deployed resources in place, which is exactly what Step 10 needs. Teams that want deletion to cascade add the finalizer back.
Expect output close to the following. Sync status reads Synced and health reads Healthy once the Deployment’s readiness probe passes and the PostSync smoke test completes.
Name: argocd/web-dev
Project: demo
Server: https://kubernetes.default.svc
Namespace: web-dev
URL: https://localhost:8443/applications/web-dev
Repo: https://github.com/YOUR_USER/gitops-demo.git
Target: HEAD
Path: apps/web/overlays/dev
Sync Policy: Automated (Prune)
Sync Status: Synced to HEAD (a1b2c3d)
Health Status: Healthy
GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE
ConfigMap web-dev web-config Synced configmap/web-config created
Service web-dev web Synced Healthy service/web created
apps Deployment web-dev web Synced Healthy deployment.apps/web created
Step 8: Change Git and watch the cluster follow
Now prove the loop. Open apps/web/overlays/dev/kustomization.yaml, change the replica value from 1 to 2, commit and push. Argo CD notices within its polling interval, marks the app OutOfSync, and applies the change on its own.
git commit -am "Scale web-dev to 2 replicas"
git push
# Skip the polling wait by forcing a refresh
argocd app get web-dev --refresh
kubectl -n web-dev get deploy web
Next, test self-healing. Scale the Deployment by hand and watch Argo CD undo your change.
kubectl -n web-dev scale deploy/web --replicas=5
kubectl -n web-dev get deploy web -w
Within seconds the replica count drops back to 2. That is the pull model at work: the cluster is not allowed to disagree with Git. For instant reactions to pushes instead of the three-minute poll, add a GitHub webhook that points at https://YOUR_ARGOCD_HOST/api/webhook. It needs a publicly reachable Argo CD URL, so it does not work with the laptop port-forward.
Step 9: Test pruning and rollback
Pruning removes resources that disappear from Git. Delete the service.yaml line from the base kustomization.yaml, commit and push. After the next sync the Service vanishes from the cluster. Restore the line and push again to bring it back.
Rollback needs one extra detail. Argo CD blocks argocd app rollback while automated sync is enabled, because auto-sync would immediately re-apply Git’s version. You have two clean options. The better one is git revert followed by a push, which keeps Git as the source of truth. The other is to pause auto-sync, roll back, and investigate.
argocd app history web-dev
# Option 1 (preferred): revert in Git
git revert HEAD --no-edit && git push
# Option 2: pause auto-sync, roll back, then re-enable later
argocd app set web-dev --sync-policy none
argocd app rollback web-dev 2
Option 2 leaves the cluster running a version that Git no longer describes. Treat it as an emergency measure, and commit the fix or revert before you re-enable automation.
Phase 4: Scale Out With ApplicationSets and Sync Waves (Steps 10-11)
Step 10: Replace the single Application with an ApplicationSet
One Application per environment does not scale. An ApplicationSet watches your repository and creates an Application for every directory that matches a pattern. Add a new overlay folder, and a new Application appears with no further clicks. This is the feature the Argo CD maintainers have been investing in during the 3.5 cycle, so it is worth learning early.
# argocd/applicationset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: web
namespace: argocd
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- git:
repoURL: https://github.com/YOUR_USER/gitops-demo.git
revision: HEAD
directories:
- path: apps/web/overlays/*
template:
metadata:
name: 'web-{{.path.basename}}'
spec:
project: demo
source:
repoURL: https://github.com/YOUR_USER/gitops-demo.git
targetRevision: HEAD
path: '{{.path.path}}'
destination:
server: https://kubernetes.default.svc
namespace: 'web-{{.path.basename}}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
The old Application and the generated one share the name web-dev, so delete the manual version first. Because it has no finalizer, the running resources stay in place and the new Application adopts them.
kubectl -n argocd delete application web-dev
git add argocd/applicationset.yaml && git commit -m "Add web ApplicationSet" && git push
kubectl apply -f argocd/applicationset.yaml
argocd app list
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY
argocd/web-dev https://kubernetes.default.svc web-dev demo Synced Healthy Auto-Prune
argocd/web-staging https://kubernetes.default.svc web-staging demo Synced Healthy Auto-Prune
To see the generator earn its keep, copy the staging folder to apps/web/overlays/qa, change the namespace inside to web-qa, commit and push. A third Application named web-qa appears after the next poll. Deleting the folder removes the Application again, though you should test that on a throwaway environment first. If you later need progressive delivery on top of this layout, our walkthrough of setting up Argo Rollouts covers canary and blue-green strategies.
Step 11: Order resources with sync waves and hooks
Kubernetes does not care about the order in which objects are created, but your app might. The ConfigMap you added in Step 3 carries wave -1, the Deployment sits at the default wave 0, and the Service uses wave 1. Argo CD applies one wave at a time and waits for each resource to become healthy before it moves to the next.
Hooks work alongside waves. The smoke-test Job has a PostSync hook, so it runs only after every other resource is Healthy. The delete policy HookSucceeded removes it after a pass and leaves a failed Job behind for debugging. You can verify the whole sequence from the CLI.
argocd app sync web-staging --prune
argocd app get web-staging --show-operation
kubectl -n web-staging get jobs
One hook caveat saves a headache. PreSync hooks run before regular resources, so a PreSync Job that needs a namespace, ServiceAccount or ConfigMap from the same Application will fail on a first-time sync. Use PostSync for checks that depend on the app, and keep PreSync for work like database migrations against infrastructure that already exists.
Phase 5: Lock It Down and Get Alerts (Steps 12-13)
Step 12: Add a CI account with narrow RBAC
The admin account can do everything, which makes it a poor choice for pipelines. Create a local account called ci that can only read and sync applications in the demo project. Set the default policy to read-only so that anyone who signs in without a role cannot change anything.
kubectl -n argocd patch configmap argocd-cm --type merge \
-p '{"data":{"accounts.ci":"apiKey"}}'
kubectl -n argocd patch configmap argocd-rbac-cm --type merge \
-p '{"data":{"policy.default":"role:readonly","policy.csv":"p, ci, applications, get, demo/*, allow\np, ci, applications, sync, demo/*, allow\n"}}'
argocd account list
TOKEN=$(argocd account generate-token --account ci)
argocd account can-i sync applications 'demo/web-dev' --auth-token "$TOKEN" --insecure --server localhost:8443
argocd app sync web-dev --auth-token "$TOKEN" --insecure --server localhost:8443
Store the token in your CI system’s secret store and set an expiry with --expires-in for anything long-lived. Once you wire up single sign-on through Dex or an external OIDC provider, consider disabling the local admin account by setting admin.enabled to "false" in argocd-cm. Confirm that your SSO login works first, or you will lock yourself out.
Declarative setup is the end goal. Both ConfigMaps above can live in Git and sync through a second Application, which makes your Argo CD configuration reviewable like any other change. The pattern is often called app-of-apps, and the advanced section returns to it.
Step 13: Send sync and health alerts to Slack
The notifications controller ships with the standard install, and the project maintains a catalog of ready-made triggers and templates. Install the catalog that matches your pinned version, add your Slack bot token, and register the Slack service. Create the bot in your workspace, give it permission to post messages, and invite it to the #gitops-alerts channel.
kubectl apply -n argocd -f \
https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/notifications_catalog/install.yaml
kubectl -n argocd patch secret argocd-notifications-secret --type merge \
-p "{\"stringData\":{\"slack-token\":\"${SLACK_TOKEN}\"}}"
kubectl -n argocd patch configmap argocd-notifications-cm --type merge \
-p '{"data":{"service.slack":"token: $slack-token\n"}}'
Then subscribe applications to triggers using annotations. Because the ApplicationSet generates every Application, add the annotations to its template once and they apply everywhere. Edit the template metadata like this, commit and push, and re-apply the file.
template:
metadata:
name: 'web-{{.path.basename}}'
annotations:
notifications.argoproj.io/subscribe.on-sync-failed.slack: gitops-alerts
notifications.argoproj.io/subscribe.on-health-degraded.slack: gitops-alerts
Break something on purpose to test the wiring. Change the image tag in the staging overlay to a tag that does not exist, push, and wait. The app turns Degraded once the pod hits ImagePullBackOff, and Slack should show a message from the bot. Revert the commit afterward. Two triggers are enough to start with. Alert fatigue kills notification setups faster than anything else, so add more only when a real incident shows you a gap.
Expected Output and Verification Checklist
Before you move on to production habits, confirm that each phase produced the result below. If a row does not match, jump to the troubleshooting section and fix it now. Problems compound quickly in GitOps because every later step assumes the earlier ones work.
| Step | Command to run | What success looks like |
|---|---|---|
| 1 | kubectl version --client | Client version prints, within one minor version of the cluster |
| 2 | kubectl get nodes | One node, status Ready |
| 4 | kubectl -n argocd get pods | Seven pods, all Running and 1/1 ready |
| 5 | argocd account get-user-info | Logged in as admin, no certificate errors with –insecure |
| 6 | argocd proj get demo | Source repo and web-* destination listed |
| 7 | argocd app get web-dev | Sync Status Synced, Health Status Healthy |
| 8 | kubectl -n web-dev get deploy web | Replica count returns to the Git value after a manual scale |
| 10 | argocd app list | web-dev and web-staging, both owned by the ApplicationSet |
| 12 | argocd account list | ci account with apiKey capability, enabled |
| 13 | Break the staging image tag | Slack message arrives within about a minute |
Also open the UI after Step 10. The Applications tile view shows both apps with green health icons, and clicking one reveals a resource tree: Application, ConfigMap, Deployment, ReplicaSet, Pod and Service. That tree is where you will spend most of your debugging time, so get comfortable reading it.
Running Argo CD on EKS: Self-Managed vs Managed Capability
Everything above runs unchanged on a managed cluster. Point kubectl at an Amazon EKS cluster, run the same install command, and the in-cluster destination https://kubernetes.default.svc keeps working. If you need to build the cluster first, our guide to setting up Amazon EKS walks through it, and AWS said Amazon EKS added Kubernetes 1.37 support on October 2, 2026, including upgrades for existing clusters.
AWS now offers a second path. Amazon EKS Capabilities includes a managed Argo CD, alongside AWS Controllers for Kubernetes and Kube Resource Orchestrator, according to AWS material dated September 2026. Managed Argo CD trades operational work for a metered fee. You skip patching the controllers, sizing Redis and planning upgrades, but you pay by the hour for the capability and again for each application it manages.
The table uses the rates from AWS’s EKS pricing page and a 730-hour month, the same month length AWS uses in its own worked examples. The monthly figures are my arithmetic from those rates. They cover control-plane and Argo CD fees only, so worker nodes, load balancers and data transfer come on top.
| Cost item | Rate | Monthly at 730 hours |
|---|---|---|
| EKS cluster, standard Kubernetes support | $0.10 per cluster-hour | $73.00 |
| EKS cluster, extended support | $0.60 per cluster-hour | $438.00 |
| Managed Argo CD capability fee | $0.03 per capability-hour | $21.90 |
| Managed Argo CD, 10 applications | $0.0015 per application-hour | Not listed |
| Managed Argo CD, 100 applications | $0.0015 per application-hour | $109.50 |
| Managed Argo CD total, 10 apps | Capability plus applications | Not listed |
| Managed Argo CD total, 100 apps | Capability plus applications | $131.40 |
| Self-managed Argo CD, any app count | No license fee | $0 plus your own compute and time |
For a team running 10 applications, managed Argo CD adds a capability charge of $0.03 per hour plus $0.0015 per application-hour, which stays modest next to the $0.10 per cluster-hour fee and the worker nodes. The real question is operational. Self-managed Argo CD carries no license fee, but according to Akuity’s comparison page you remain responsible for infrastructure, upgrades, scaling, high availability and recovery. If your platform team already runs plenty of other controllers, one more is cheap. If not, the managed option removes a whole category of 2 a.m. pages.
Check one caution before you decide. Version support matters because a cluster in standard support costs $0.10 an hour and one in extended support costs $0.60, so an upgrade backlog is a budget problem as well as a security one. Our breakdown of the EKS cluster fee against ECS shows how that fee shapes the wider AWS container decision.
AWS also said on October 28, 2026 that EKS Auto Mode added support for the Kubernetes Gateway API, with Gateway and HTTPRoute resources provisioning the load balancing underneath. That gives you a clean way to expose the Argo CD UI on EKS without hand-built Ingress objects. Standard AWS load balancer charges still apply.
7 Common Pitfalls That Break Argo CD Setups
Most failed installs trace back to a short list of habits. Check this list before you blame the tool.
- Applying the install manifest client-side. Plain
kubectl applycan fail on large custom resource definitions. Use--server-sideevery time, including on upgrades. - Pointing at a moving tag. A URL containing
stableormasterchanges under you. Pin an exact release tag and upgrade deliberately. - Enabling prune without understanding it. With
prune: true, deleting a file in Git deletes the live resource. Test on dev first and keep PVC-backed apps on manual prune. - Fighting an autoscaler. If an HPA changes replicas and Git says otherwise,
selfHealresets them in a loop. Add anignoreDifferencesrule for/spec/replicas. - Committing secrets to Git. Base64 is not encryption. Use Sealed Secrets, External Secrets Operator or SOPS, and rotate anything that already landed in history.
- Leaving everything in the default project. The default project trusts any repo and any namespace. Create scoped projects early, because retrofitting them later means touching every Application.
- Expecting instant syncs. The default polling interval is about three minutes. Engineers who push and then see nothing assume the tool is broken. Use a refresh or add a webhook.
One more habit belongs on the list. Do not run kubectl apply on resources that Argo CD owns. It works once, then Argo CD reverts you, and the confusing back-and-forth wastes an afternoon. Change Git instead, or temporarily disable auto-sync on the specific Application.
Troubleshooting: 10 Argo CD Errors and Fixes
These ten problems account for most of the support threads you will find. Each entry names the symptom, the cause and the fix.
- Error: metadata.annotations: Too long during install. You used client-side apply. Re-run the install with
--server-side --force-conflicts. - Application stays OutOfSync even after a sync. A controller or admission webhook mutates a field that Git does not specify, often replicas or injected sidecars. Run
argocd app diff web-devto find the field, then add anignoreDifferencesentry for it. - ComparisonError: authentication required. The repo URL is wrong or credentials are missing. Run
argocd repo list, confirm the URL ends the same way as in the Application, and re-add the repo with a valid token. - application destination is not permitted in project. The AppProject does not list the namespace. Add a matching entry to
spec.destinationsand re-apply the project. - namespaces “web-dev” not found. Add
CreateNamespace=truetosyncOptions, and make sure the project whitelists the Namespace kind. - App stuck in Progressing. Sync worked but a resource is not healthy. Check
kubectl -n web-dev describe podfor ImagePullBackOff, a failing probe or insufficient resources on the kind node. - Application will not delete. The resources finalizer is waiting on a child that cannot be removed. Fix or delete the stuck child, or as a last resort patch the finalizer away with
kubectl patch app NAME -n argocd --type merge -p '{"metadata":{"finalizers":null}}'. - x509: certificate signed by unknown authority on login. Argo CD generated a self-signed certificate. Add
--insecurefor local work, or install a trusted certificate before real use. - ApplicationSet generates no Applications. The directory glob matches nothing or the repo is unreachable. Check
kubectl -n argocd logs deploy/argocd-applicationset-controllerand confirm the path pattern matches your folders exactly. - argocd-initial-admin-secret not found. You already deleted it, or the password was changed. Reset the admin password by patching
argocd-secretwith a new bcrypt hash, as described in the upstream docs, then restart the server pod.
When none of these fit, read the controller logs first. kubectl -n argocd logs statefulset/argocd-application-controller and the repo-server logs explain nearly every failure in plain language. The UI event list under each Application is the second place to look.
Advanced Tips for Production GitOps
Once the basics work, these upgrades pay off fastest. Pick the ones that match your risk, not all at once.
Use webhooks instead of polling. Add a webhook in your Git host that posts to /api/webhook on the Argo CD server. Syncs begin within seconds of a merge, and the repo server stops hammering Git on a timer.
Ignore fields owned by other controllers. The block below stops Argo CD from resetting replicas that an HPA controls.
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
Bootstrap with app-of-apps. A single root Application can watch the argocd/ folder and apply your project, ApplicationSet and configuration. Then a brand-new cluster needs only one manual command.
# bootstrap/root.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/YOUR_USER/gitops-demo.git
targetRevision: HEAD
path: argocd
directory:
recurse: true
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
Prepare for upgrades. The Argo CD 3.6 release candidate is out, and write-ups such as the one on preparing for the Argo CD 3.6 upgrade cover server-side diff and related behavior changes that may affect your Applications. Read them, test in a throwaway cluster, and wait for a stable tag before touching production.
Check your Helm assumptions. Helm 4 is now the current major line, with 4.3.1 released on October 15, 2026, and Helm 3 security fixes end on February 10, 2027, according to the Helm project. If your Applications render Helm charts, confirm which Helm version your Argo CD release bundles and test chart rendering before you upgrade either side. Our Helm 3 to Helm 4 migration guide lists the chart changes to look for.
Manage more than one cluster. Register each extra cluster with argocd cluster add CONTEXT_NAME, then point ApplicationSet generators at the cluster list. One Git repo can then drive dev, staging and production clusters from a single control plane. Many teams run a dedicated management cluster for Argo CD so an application outage cannot take down the deployment tool.
Run in high availability. The project ships an HA manifest alongside the standard one. Use it once you have more than a handful of Applications, and size Redis and the repo server for your repository count rather than your app count.
Gate changes before merge. Run kubectl kustomize or helm template plus a schema check such as kubeconform in CI. Cheap checks on the pull request stop broken manifests before Argo CD ever sees them. OpenShift teams should also compare notes with our look at OpenShift versus upstream Kubernetes, since GitOps tooling choices differ there.
The Full Working Project: Files and One-Shot Bootstrap Script
Here is the finished repository layout, followed by a script that rebuilds the whole environment on a fresh machine. Replace the two placeholders at the top, push the repository first, and run the script. It takes about four minutes once Docker has pulled the images.
gitops-demo/
apps/web/base/
configmap.yaml # sync-wave -1
deployment.yaml # wave 0 (default)
service.yaml # sync-wave 1
smoke-test.yaml # PostSync hook
kustomization.yaml
apps/web/overlays/
dev/kustomization.yaml # namespace web-dev, 1 replica
staging/kustomization.yaml # namespace web-staging, 2 replicas
argocd/
project.yaml
applicationset.yaml
bootstrap/
root.yaml
#!/usr/bin/env bash
set -euo pipefail
GITHUB_USER="YOUR_USER"
ARGOCD_VERSION="v3.5.6" # use the newest stable tag from the releases page
REPO="https://github.com/${GITHUB_USER}/gitops-demo.git"
kind create cluster --name gitops
kubectl create namespace argocd
kubectl apply -n argocd --server-side --force-conflicts \
-f "https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/manifests/install.yaml"
kubectl -n argocd rollout status deploy/argocd-server --timeout=300s
kubectl -n argocd rollout status deploy/argocd-applicationset-controller --timeout=300s
# Point the manifests at your fork, then let Argo CD take over
sed -i.bak "s#https://github.com/YOUR_USER/gitops-demo.git#${REPO}#g" argocd/*.yaml bootstrap/*.yaml
kubectl apply -f argocd/project.yaml
kubectl apply -f argocd/applicationset.yaml
echo "Initial admin password:"
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d; echo
echo "Open the UI with: kubectl -n argocd port-forward svc/argocd-server 8443:443"
Tear everything down when you finish with kind delete cluster --name gitops. The Git repository keeps the entire desired state, so rebuilding takes minutes. That is the real payoff of this approach, and it applies equally to a production cluster after a bad day. For more cluster-level guides, browse our cloud computing coverage.
Argo CD Tutorial FAQ
What is Argo CD used for?
Argo CD deploys and maintains Kubernetes applications by treating a Git repository as the source of truth. It compares the manifests in Git with what runs in the cluster, reports differences and, if you allow it, fixes them automatically.
Is Argo CD free?
Yes. The project is open source under the Apache 2.0 license and graduated from the CNCF in 2022, according to Akuity. Self-managed installs carry no license fee, though you pay for the compute that runs them. Managed offerings, including the AWS capability priced above, charge by usage.
Which Kubernetes versions should I run it on?
Use a version the Kubernetes project still supports. As of October 29, 2026, that means 1.37, 1.36 or 1.35. Check the Argo CD documentation for the tested Kubernetes versions of your chosen Argo CD release, since the two projects release on separate schedules.
How often does Argo CD sync from Git?
By default the repo server checks for changes about every three minutes. A Git provider webhook cuts that to a few seconds, and argocd app get NAME --refresh forces an immediate check.
How should I store secrets with Argo CD?
Do not put plain secrets in Git. Encrypt them with Sealed Secrets or SOPS, or keep them in an external store and sync them with the External Secrets Operator. Argo CD then manages the encrypted object or the reference, never the raw value.
Should I run the Argo CD 3.6 release candidate?
No, not in production. A 3.6.0 release candidate (rc4) was published on October 22, 2026, and release candidates can change before the final tag. Test it in a disposable cluster if you want an early look, and run the newest stable release everywhere else.
Can Argo CD deploy Helm charts?
Yes. Point an Application’s source at a chart in Git or a chart repository and supply values in the manifest. The repo server renders the chart, and Argo CD applies the result like any other manifest. Confirm the Helm version your Argo CD release bundles before relying on Helm 4 behavior.





