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.
  • kubectl installed on your workstation (Ubuntu 24.04 in the examples) and a kubeconfig with cluster-admin rights. Check it with kubectl get nodes.
  • A Git repository with Kubernetes manifests. The tutorial uses the public argoproj/argocd-example-apps repository, so you can start without your own.
  • Optional, for Step 6: an ingress controller, cert-manager and a DNS A record for argocd.your_domain pointing 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.

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: the guestbook namespace 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 long when installing: you ran kubectl apply without --server-side. Run the command from Step 1 again with the flag.
  • Application shows ComparisonError or authentication required: the repo server cannot read the repository. Check the URL and credentials with argocd repo list and re-add the repository with a valid token.
  • Application stays OutOfSync right after a sync: a controller or webhook in the cluster is changing a field Argo CD manages (a common case is replicas set by a HorizontalPodAutoscaler). Use argocd app diff your_app to see the field and remove it from the manifest or add an ignoreDifferences entry for it.
  • Sync or health problems you cannot explain: read the controller logs with kubectl logs -n argocd statefulset/argocd-application-controller and the repo server logs with kubectl 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.