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.yamlThis 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"=trueThe 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: truehelm install argocd argo/argo-cd -n argocd --create-namespace -f argocd-values.yamlCLI 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> --insecureChange the admin password immediately after first login:
argocd account update-passwordConnecting 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_xxxxxxxxxxxxCreating 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=trueApply 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-healThe Application CRD Structure#
Key fields in the Application spec:
spec.project– The ArgoCD project scoping this app.defaultallows 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. UseHEADfor the default branch.spec.source.path– Directory within the repo containing manifests.spec.destination.server– The Kubernetes API server URL.https://kubernetes.default.svcmeans 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-appAutomated sync applies changes automatically when Git changes:
syncPolicy:
automated:
prune: false
selfHeal: falseprune: 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 (viakubectl 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 --syncIn 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#
- Forgetting
CreateNamespace=truein sync options. If the target namespace does not exist and this option is not set, sync fails with a namespace-not-found error. - Leaving prune disabled indefinitely. Old resources accumulate in the cluster, diverging from Git.
- Not changing the initial admin password. The password is stored in a Kubernetes secret anyone with namespace access can read.
- Using
targetRevision: HEADin production. This tracks the default branch, which means any merge to main immediately deploys. Pin to tags or specific branches for production apps.