How to Deploy with Argo CD GitOps
Implement GitOps continuous deployment with Argo CD. Sync Kubernetes manifests from Git repositories automatically with declarative application management.
π‘ Quick Answer: Install Argo CD (
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml), create anApplicationCRD pointing to your Git repo, and Argo CD automatically syncs manifests to your cluster. Changes in Git trigger deployments.Key command:
argocd app create my-app --repo https://github.com/org/repo --path k8s --dest-server https://kubernetes.default.svcGotcha: Enable auto-sync with
syncPolicy.automatedfor true GitOps; manual sync is default. UseselfHeal: trueto revert manual cluster changes.
Argo CD is a declarative GitOps continuous delivery tool. It monitors Git repositories and automatically syncs application state to your Kubernetes cluster.
Install Argo CD
# Create namespace
kubectl create namespace argocd
# Install Argo CD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Wait for pods to be ready
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300sAccess Argo CD UI
# Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# Port-forward to UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Login via CLI
argocd login localhost:8080 --username admin --password <password> --insecureCreate Application via YAML
# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/my-app-manifests
targetRevision: HEAD
path: k8s/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Delete resources removed from Git
selfHeal: true # Revert manual changes
syncOptions:
- CreateNamespace=trueCreate Application via CLI
argocd app create my-app \
--repo https://github.com/myorg/my-app-manifests \
--path k8s/overlays/production \
--dest-server https://kubernetes.default.svc \
--dest-namespace production \
--sync-policy automated \
--auto-prune \
--self-healApplication with Helm
# helm-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-helm-app
namespace: argocd
spec:
project: default
source:
repoURL: https://charts.example.com
chart: my-chart
targetRevision: 1.2.3
helm:
values: |
replicaCount: 3
image:
tag: v2.0.0
parameters:
- name: service.type
value: LoadBalancer
destination:
server: https://kubernetes.default.svc
namespace: productionApplication with Kustomize
# kustomize-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-kustomize-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/my-app
targetRevision: main
path: k8s/overlays/production
kustomize:
images:
- my-app=my-registry/my-app:v2.0.0
destination:
server: https://kubernetes.default.svc
namespace: productionApplicationSet for Multiple Environments
# applicationset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: my-app
namespace: argocd
spec:
generators:
- list:
elements:
- env: dev
namespace: dev
- env: staging
namespace: staging
- env: production
namespace: production
template:
metadata:
name: 'my-app-{{env}}'
spec:
project: default
source:
repoURL: https://github.com/myorg/my-app
targetRevision: HEAD
path: 'k8s/overlays/{{env}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{namespace}}'
syncPolicy:
automated:
prune: trueSync Waves and Hooks
# Use annotations to control sync order
apiVersion: v1
kind: Namespace
metadata:
name: my-app
annotations:
argocd.argoproj.io/sync-wave: "-1" # Create first
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
annotations:
argocd.argoproj.io/sync-wave: "0" # Then deployment
---
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
annotations:
argocd.argoproj.io/hook: PreSync # Run before sync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
---
apiVersion: batch/v1
kind: Job
metadata:
name: smoke-test
annotations:
argocd.argoproj.io/hook: PostSync # Run after sync succeeds
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: test
image: my-app:latest
command: ["./smoke-test.sh"]
restartPolicy: NeverAdditional useful sync options beyond CreateNamespace=true:
spec:
syncPolicy:
syncOptions:
- PrunePropagationPolicy=foreground
- PruneLast=true
- RespectIgnoreDifferences=true
- ApplyOutOfSyncOnly=true
- ServerSideApply=trueApp of Apps Pattern
Manage a fleet of Applications by having a root Application point at a directory of other Application manifests:
# apps/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root-app
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/myorg/my-app-manifests
targetRevision: main
path: apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true# apps/my-app.yaml (child Application managed by root-app)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/myorg/my-app-manifests
targetRevision: main
path: manifests/my-app
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: trueMulti-Cluster Management
Register an external cluster so Argo CD can deploy to it:
# Add cluster via CLI
argocd cluster add <context-name> --name production-cluster
# Or via Secret
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: production-cluster
namespace: argocd
labels:
argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
name: production-cluster
server: https://production.k8s.example.com
config: |
{
"bearerToken": "<service-account-token>",
"tlsClientConfig": {
"insecure": false,
"caData": "<base64-ca-cert>"
}
}
EOFFan an Application out to every registered cluster with the clusters generator:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: multi-cluster-app
namespace: argocd
spec:
generators:
- clusters: {} # All registered clusters
template:
metadata:
name: 'my-app-{{name}}'
spec:
project: default
source:
repoURL: https://github.com/myorg/my-app-manifests
path: k8s/overlays/production
targetRevision: main
destination:
server: '{{server}}'
namespace: productionProjects and RBAC
Scope which repos, clusters, and resource kinds an Application can use with an AppProject, and control who can sync it:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
description: Production applications
sourceRepos:
- https://github.com/myorg/my-app-manifests
destinations:
- namespace: production
server: https://kubernetes.default.svc
clusterResourceWhitelist:
- group: ""
kind: Namespace
namespaceResourceWhitelist:
- group: "*"
kind: "*"
roles:
- name: developer
description: Developer access
policies:
- p, proj:production:developer, applications, get, production/*, allow
- p, proj:production:developer, applications, sync, production/*, allow
groups:
- developersapiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
data:
policy.default: role:readonly
policy.csv: |
p, role:admin, applications, *, */*, allow
p, role:developer, applications, sync, */*, allow
g, admins, role:admin
g, developers, role:developerMonitoring and Notifications
Send Slack alerts on sync status changes with the Argo CD notifications controller:
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
namespace: argocd
data:
service.slack: |
token: $slack-token
template.app-sync-status: |
message: |
Application {{.app.metadata.name}} sync status: {{.app.status.sync.status}}
Health: {{.app.status.health.status}}
trigger.on-health-degraded: |
- when: app.status.health.status == 'Degraded'
send: [app-sync-status]apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
annotations:
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: deployments
notifications.argoproj.io/subscribe.on-sync-failed.slack: deploymentsCLI Commands
# List applications
argocd app list
# Get app status
argocd app get my-app
# Sync application
argocd app sync my-app
# View app diff
argocd app diff my-app
# Rollback
argocd app rollback my-app
# Delete app (keeps resources)
argocd app delete my-app --cascade=falseBest Practices
- Use separate repos for app code and manifests
- Enable auto-sync for true GitOps
- Use sync waves for ordered deployments
- Implement RBAC with Argo CD projects
- Store secrets with Sealed Secrets or External Secrets

Recommended
Kubernetes Recipes β The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book βLearn by Doing
CopyPasteLearn β Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses βπ Deepen Your Skills β Hands-on Courses
Courses by CopyPasteLearn.com β Learn IT by Doing
