Argo CD is a GitOps continuous delivery controller for Kubernetes. It watches a Git repository that contains your manifests (plain YAML, Kustomize or Helm) and keeps the cluster in the state described there, reporting any drift. In this tutorial you will install Argo CD with the official manifests, log in with the argocd CLI, deploy a sample application from Git and turn on automated sync so every commit is applied without running kubectl by hand.
Prerequisites
To follow this tutorial you need:
- A running Kubernetes cluster (version 1.30 or newer), for example a CubePath managed Kubernetes cluster or a self-managed k3s/kubeadm cluster, with at least 2 GB of free memory for the Argo CD components.
kubectlinstalled on your workstation (Ubuntu 24.04 in the examples) and a kubeconfig with cluster-admin rights. Check it withkubectl get nodes.- A Git repository with Kubernetes manifests. The tutorial uses the public
argoproj/argocd-example-appsrepository, so you can start without your own. - Optional, for Step 6: an ingress controller, cert-manager and a DNS A record for
argocd.your_domainpointing to the ingress load balancer.
Step 1 - Installing Argo CD
Argo CD runs in its own namespace. Create it:
kubectl create namespace argocd
Apply the official manifest for the latest stable release. The --server-side flag is required because some Argo CD CRDs are larger than the annotation size limit that client-side kubectl apply uses:
kubectl apply -n argocd --server-side --force-conflicts \
-f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Wait until all pods report Ready:
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s
Then list them:
kubectl get pods -n argocd
NAME READY STATUS RESTARTS AGE
argocd-application-controller-0 1/1 Running 0 2m
argocd-applicationset-controller-6d8f7c9b5d-x2kqz 1/1 Running 0 2m
argocd-dex-server-5c8b8d7f9d-7lq4m 1/1 Running 0 2m
argocd-notifications-controller-7b9d8c6f4b-9hz2p 1/1 Running 0 2m
argocd-redis-6f9c5b8d7c-kq8wd 1/1 Running 0 2m
argocd-repo-server-7f8d9c6b5f-p4tnm 1/1 Running 0 2m
argocd-server-5d7b9f8c6d-vw3rj 1/1 Running 0 2m
The argocd-server pod serves the web UI and API, the repo-server renders manifests from Git, and the application-controller compares and syncs them with the cluster.
NoteFor production you can pin a specific release instead of
stableby replacingstablein the URL with a tag such asv3.1.0. Pinning makes upgrades an explicit, reviewable change.
Step 2 - Installing the Argo CD CLI
The argocd CLI talks to the Argo CD API server and is the quickest way to create and inspect applications. Download the latest Linux binary (use argocd-linux-arm64 on ARM machines):
curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
rm argocd-linux-amd64
Verify the client:
argocd version --client --short
argocd: v3.1.0+...
Step 3 - Logging in and changing the admin password
The API server is not exposed outside the cluster yet. Open a port-forward in a separate terminal and leave it running:
kubectl port-forward svc/argocd-server -n argocd 8080:443
During installation Argo CD generates a random password for the admin user and stores it in a secret. Print it:
argocd admin initial-password -n argocd
Xk3pQ9vLr2TbYw7A
This password must be only used for first time login. We strongly recommend you update the password using `argocd account update-password`.
Log in through the port-forward. The --insecure flag is needed because Argo CD uses a self-signed certificate by default:
argocd login localhost:8080 --username admin --insecure
Set a new password and then delete the secret that holds the initial one, since it is no longer used:
argocd account update-password
kubectl -n argocd delete secret argocd-initial-admin-secret
You can also open https://localhost:8080 in a browser while the port-forward is active and log in to the web UI with the same credentials.
Step 4 - Deploying an application from Git
An Argo CD Application links a path in a Git repository to a namespace in a cluster. Create one for the guestbook example with the CLI:
argocd app create guestbook \
--repo https://github.com/argoproj/argocd-example-apps.git \
--path guestbook \
--dest-server https://kubernetes.default.svc \
--dest-namespace default
https://kubernetes.default.svc refers to the cluster Argo CD itself runs in. The application is created but not deployed yet, so its status is OutOfSync. Sync it:
argocd app sync guestbook
Check the result:
argocd app get guestbook
Name: argocd/guestbook
Project: default
Server: https://kubernetes.default.svc
Namespace: default
Repo: https://github.com/argoproj/argocd-example-apps.git
Target:
Path: guestbook
Sync Policy: Manual
Sync Status: Synced to (a1b2c3d)
Health Status: Healthy
GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE
Service default guestbook-ui Synced Healthy service/guestbook-ui created
apps Deployment default guestbook-ui Synced Healthy deployment.apps/guestbook-ui created
Synced means the cluster matches Git, and Healthy means the resources are running correctly. Remove this test application before moving on; deleting it also deletes the resources it created:
argocd app delete guestbook --yes
If your manifests are in a private repository, register it first with a read-only access token:
argocd repo add https://github.com/your_org/your_repo.git --username your_user --password your_token
Step 5 - Defining the application declaratively with automated sync
Creating applications with the CLI is handy for testing, but in a GitOps workflow the Application itself should also be a file under version control. Create guestbook-app.yaml:
nano guestbook-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
destination:
server: https://kubernetes.default.svc
namespace: guestbook
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
The syncPolicy block is what turns this into continuous delivery:
automated: Argo CD syncs on its own when the Git revision changes (it polls every 3 minutes by default).prune: true: resources removed from Git are deleted from the cluster.selfHeal: true: manual changes in the cluster are reverted to the state in Git.CreateNamespace=true: theguestbooknamespace is created if it does not exist.
Apply it:
kubectl apply -f guestbook-app.yaml
After a few seconds the application is synced into the new namespace:
kubectl get deployment -n guestbook
NAME READY UP-TO-DATE AVAILABLE AGE
guestbook-ui 1/1 1 1 30s
Test self-healing by changing the deployment by hand:
kubectl scale deployment guestbook-ui -n guestbook --replicas=3
Watch the replica count for a few seconds:
kubectl get deployment guestbook-ui -n guestbook -w
Argo CD detects the drift and sets the replica count back to 1, the value in Git. From now on, the way to change this application is a commit to the repository.
Step 6 - Exposing the web UI with an Ingress (optional)
A port-forward is fine for administration from one machine. For team access, publish Argo CD through your ingress controller and let the ingress terminate TLS. First tell argocd-server to serve plain HTTP inside the cluster, then restart it:
kubectl -n argocd patch configmap argocd-cmd-params-cm --type merge -p '{"data":{"server.insecure":"true"}}'
kubectl -n argocd rollout restart deployment argocd-server
Create argocd-ingress.yaml. Replace your_domain, set ingressClassName to the class your controller uses (kubectl get ingressclass) and adjust the issuer name to your cert-manager ClusterIssuer:
nano argocd-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server
namespace: argocd
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
rules:
- host: argocd.your_domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
name: http
tls:
- hosts:
- argocd.your_domain
secretName: argocd-server-tls
Apply it and check that the certificate is issued:
kubectl apply -f argocd-ingress.yaml
kubectl get certificate -n argocd
NAME READY SECRET AGE
argocd-server-tls True argocd-server-tls 1m
Open https://argocd.your_domain in a browser. Most ingress controllers do not pass Argo CD's gRPC traffic through a plain HTTP route, so log in with the CLI in gRPC-Web mode:
argocd login argocd.your_domain --grpc-web
To have Argo CD sync immediately on push instead of waiting for the next poll, add a webhook in your Git provider pointing to https://argocd.your_domain/api/webhook with content type application/json and the push event.
Troubleshooting
metadata.annotations: Too longwhen installing: you rankubectl applywithout--server-side. Run the command from Step 1 again with the flag.- Application shows
ComparisonErrororauthentication required: the repo server cannot read the repository. Check the URL and credentials withargocd repo listand re-add the repository with a valid token. - Application stays
OutOfSyncright after a sync: a controller or webhook in the cluster is changing a field Argo CD manages (a common case isreplicasset by a HorizontalPodAutoscaler). Useargocd app diff your_appto see the field and remove it from the manifest or add anignoreDifferencesentry for it. - Sync or health problems you cannot explain: read the controller logs with
kubectl logs -n argocd statefulset/argocd-application-controllerand the repo server logs withkubectl logs -n argocd deployment/argocd-repo-server.
Conclusion
You installed Argo CD, secured the admin account, deployed an application from Git and enabled automated sync with pruning and self-healing, so the Git repository is now the source of truth for that workload. Next, move your own manifests into a repository and create an Application per service, add an AppProject to restrict which repositories and namespaces each team can use, and configure SSO through Dex or your OIDC provider instead of the local admin account.