ArgoCD Setup and Basics#

ArgoCD is a declarative GitOps continuous delivery tool for Kubernetes. It watches Git repositories and ensures your cluster state matches what is declared in those repos. When someone changes a manifest in Git, ArgoCD detects the drift and either alerts you or automatically applies the change.

Installation with Plain Manifests#

The fastest path to a running ArgoCD instance:

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

This installs the full ArgoCD stack: API server, repo server, application controller, Redis, and Dex (for SSO). For a minimal install without SSO and the UI, use namespace-install.yaml instead, which also scopes ArgoCD to a single namespace.

Installation with Helm#

For production, Helm gives you control over every component:

helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

helm install argocd argo/argo-cd \
  --namespace argocd \
  --create-namespace \
  --set server.service.type=LoadBalancer \
  --set configs.params."server\.insecure"=true

The server.insecure flag disables TLS on the ArgoCD API server itself, which is appropriate when you terminate TLS at an ingress controller or load balancer. Do not use this in environments where traffic between the ingress and ArgoCD is untrusted.

A values file for production typically looks like:

# argocd-values.yaml
server:
  replicas: 2
  service:
    type: ClusterIP
  ingress:
    enabled: true
    hostname: argocd.example.com
    tls: true
controller:
  replicas: 1
repoServer:
  replicas: 2
configs:
  params:
    server.insecure: true
helm install argocd argo/argo-cd -n argocd --create-namespace -f argocd-values.yaml

CLI Setup#

Install the ArgoCD CLI:

# macOS
brew install argocd

# Linux
curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd && sudo mv argocd /usr/local/bin/

Get the initial admin password and log in:

# The initial password is stored in a secret
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

# If ArgoCD is not exposed externally, port-forward first
kubectl port-forward svc/argocd-server -n argocd 8080:443 &

argocd login localhost:8080 --username admin --password <password> --insecure

Change the admin password immediately after first login:

argocd account update-password

Connecting a Git Repository#

ArgoCD needs access to the Git repo containing your manifests:

# Public repo (no credentials needed)
argocd repo add https://github.com/myorg/k8s-manifests.git

# Private repo with SSH key
argocd repo add git@github.com:myorg/k8s-manifests.git --ssh-private-key-path ~/.ssh/deploy_key

# Private repo with HTTPS token
argocd repo add https://github.com/myorg/k8s-manifests.git --username git --password <token>

You can also declare repos as Kubernetes secrets directly:

apiVersion: v1
kind: Secret
metadata:
  name: my-repo
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
stringData:
  type: git
  url: https://github.com/myorg/k8s-manifests.git
  username: git
  password: ghp_xxxxxxxxxxxx

Creating Your First Application#

An ArgoCD Application is a CRD that connects a Git source to a cluster destination. Here is the full structure:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/k8s-manifests.git
    targetRevision: main
    path: apps/my-app
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Apply it with kubectl apply -f application.yaml or create it via CLI:

argocd app create my-app \
  --repo https://github.com/myorg/k8s-manifests.git \
  --path apps/my-app \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace my-app \
  --revision main \
  --sync-policy automated \
  --auto-prune \
  --self-heal

The Application CRD Structure#

Key fields in the Application spec:

  • spec.project – The ArgoCD project scoping this app. default allows everything. Custom projects restrict which repos, clusters, and namespaces an app can use.
  • spec.source.repoURL – The Git repository URL.
  • spec.source.targetRevision – Branch, tag, or commit SHA. Use HEAD for the default branch.
  • spec.source.path – Directory within the repo containing manifests.
  • spec.destination.server – The Kubernetes API server URL. https://kubernetes.default.svc means the cluster where ArgoCD is installed.
  • spec.destination.namespace – Target namespace for deployed resources.

For Helm sources, replace path with chart information:

source:
  repoURL: https://charts.example.com
  chart: my-chart
  targetRevision: 1.2.3
  helm:
    valuesFiles:
      - values.yaml
      - values-prod.yaml
    parameters:
      - name: image.tag
        value: "v1.0.0"

Sync Policies#

Manual sync is the default. ArgoCD detects drift and marks the app as OutOfSync, but takes no action. You trigger sync explicitly:

argocd app sync my-app

Automated sync applies changes automatically when Git changes:

syncPolicy:
  automated:
    prune: false
    selfHeal: false
  • prune: true – Delete resources from the cluster that no longer exist in Git. Without this, ArgoCD only adds and updates but never removes resources. Enable this for true GitOps, but be aware that removing a manifest from Git will delete the live resource.
  • selfHeal: true – If someone manually changes a resource in the cluster (via kubectl edit, for example), ArgoCD reverts it back to the Git-defined state. This enforces Git as the single source of truth.

Both prune and selfHeal default to false even when automated sync is enabled. This is intentional – it prevents accidental deletions and lets teams adopt GitOps incrementally.

Verifying Your Setup#

After creating an application, check its status:

argocd app get my-app
argocd app list

# Watch sync status
argocd app wait my-app --sync

In the UI (accessible via the port-forward or ingress), you get a visual dependency graph of all Kubernetes resources managed by the application. This is one of ArgoCD’s most useful features for understanding what is actually deployed.

Common Mistakes#

  1. Forgetting CreateNamespace=true in sync options. If the target namespace does not exist and this option is not set, sync fails with a namespace-not-found error.
  2. Leaving prune disabled indefinitely. Old resources accumulate in the cluster, diverging from Git.
  3. Not changing the initial admin password. The password is stored in a Kubernetes secret anyone with namespace access can read.
  4. Using targetRevision: HEAD in production. This tracks the default branch, which means any merge to main immediately deploys. Pin to tags or specific branches for production apps.