ArgoCD Sync Strategies
Review baseline: Argo CD 3.5.2; updated to patch 3.5.4 after release-note review (examples not re-run) Last Updated: October 12, 2026
Table of Contents
- Manual vs Automated Sync
- Auto-Sync Policies
- Sync Options
- Sync Waves and Phases
- Sync Windows
- Diffing Customization
- Retry Policies
- Selective Sync
Manual vs Automated Sync
Examples are independent policy fragments. Keep a real Application's source/destination/project and select only needed options. Sync applies changes; Refresh updates comparison from sources/cache. Synced is separate from health and actual service availability.
ArgoCD supports two synchronization modes: manual and automated.
Manual Sync
In manual mode, users must explicitly trigger synchronization:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: manual-sync-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/myrepo.git
targetRevision: HEAD
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: my-app
# No syncPolicy.automated = manual syncTriggering manual sync:
# Via CLI
argocd app sync my-app
# Sync specific resources only
argocd app sync my-app --resource 'apps:Deployment:my-deployment'
# Preview changes; pruning and force are separate, potentially destructive decisions
argocd app sync my-app --dry-runAutomated Sync
In automated mode, ArgoCD automatically syncs when changes are detected:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: auto-sync-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/myrepo.git
targetRevision: HEAD
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
automated: {} # Enable auto-sync with defaultsAuto-Sync Policies
automated: {} or an omitted/null enabled flag enables auto-sync; enabled: false disables it. Live-only drift correction requires selfHeal, and prune is separate. Failed identical commit/parameter combinations are not retried forever by default; configure retry policy. Change the owning template for ApplicationSet-managed Applications.
Prune
Automatically delete resources that no longer exist in Git:
syncPolicy:
automated:
prune: trueUse case: Ensure cluster state exactly matches Git repository. Removes orphaned resources.
Prune affects the Application's tracked resources, not every orphan in the cluster. PruneLast changes ordering; it does not protect data or replace review of deletions.
Self-Heal
Automatically revert manual changes made to the cluster:
syncPolicy:
automated:
selfHeal: trueUse case: Prevent configuration drift from manual kubectl changes or other tools.
Allow Empty
Allow applications with no resources:
syncPolicy:
automated:
allowEmpty: trueWith automated prune enabled, allowEmpty can permit deleting every managed resource when rendering returns an empty set. It is not a generic bootstrap requirement.
Complete Auto-Sync Configuration
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: fully-automated-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/myrepo.git
targetRevision: HEAD
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
automated:
prune: true # Remove orphaned resources
selfHeal: true # Revert manual changes
allowEmpty: false # Prevent auto-prune from deleting everything on an empty renderSync Options
Sync options provide fine-grained control over synchronization behavior.
Available Sync Options
| Option | Description | Default |
|---|---|---|
Validate | Validate resources against schema | true |
CreateNamespace | Create namespace if missing | false |
PrunePropagationPolicy | Deletion propagation policy | foreground |
PruneLast | Prune after all other syncs | false |
Replace | Use replace instead of apply | false |
FailOnSharedResource | Fail if another Argo CD Application tracks the resource | false |
ApplyOutOfSyncOnly | Only apply out-of-sync resources | false |
ServerSideApply | Use server-side apply | false |
RespectIgnoreDifferences | Respect ignoreDifferences in sync | false |
Validate=false skips apply-time schema validation; it does not install a missing CRD. Argo CD automatically skips a CR's dry run when its CRD is installed in the same sync. Use SkipDryRunOnMissingResource=true only for a justified case such as a CRD created by another controller, and verify that the CRD is available.
PruneLast=true schedules pruning in a final implicit wave after other resources are deployed and Healthy. FailOnSharedResource=true checks tracking by another Argo CD Application; it does not detect every ownership conflict with other Kubernetes controllers or Flux.
Application-Level Options
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/myrepo.git
targetRevision: main
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
- Validate=true
- ApplyOutOfSyncOnly=trueResource-Level Options
Apply sync options to specific resources via annotations:
# Merge into an existing complete resource manifest.
metadata:
annotations:
argocd.argoproj.io/sync-options: ServerSideApply=trueServer-Side Apply
Argo CD 3.5.4 invokes --server-side --force-conflicts. Field ownership is tracked, but this is not a guarantee that conflicts will be rejected; review possible ownership takeover.
Use Kubernetes server-side apply for better conflict detection:
syncPolicy:
syncOptions:
- ServerSideApply=trueOr per resource:
metadata:
annotations:
argocd.argoproj.io/sync-options: ServerSideApply=trueBenefits:
- Better field ownership tracking
- Ownership conflicts can be force-resolved by Argo CD; inspect the intended manager boundaries
- Works well with CRDs and webhooks
Replace and Force
Replace selects kubectl replace/create. It does not automatically make immutable fields editable. Force=true with Replace=true can delete/recreate objects and is a separate destructive choice:
metadata:
annotations:
argocd.argoproj.io/sync-options: Replace=trueImmutable changes need a resource-specific migration/recreation plan. Do not use this as a shortcut for PVC storage-class migration. Review retention/data/availability before deletion. Replace takes precedence over ServerSideApply.
Sync Waves and Phases
Sync waves control the order in which resources are applied.
How Waves Work
Resources are grouped by wave number and synced in order:
- Sort by phase, then wave, kind and name.
- A negative Sync wave does not run before the PreSync phase.
- Wave advancement follows sync/health state; do not depend on same-wave physical concurrency for dependencies. Custom resources need meaningful health checks when readiness must block later work.
Setting Sync Wave
Merge this fragment into the metadata of a complete resource manifest. A complete example follows.
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1"Combining Waves with Hooks
PreSync runs before every ordinary Sync wave. The existing service, namespace, DB Secret and migration image below must already be prepared; a PreSync cannot assume access to a database created in a later Sync wave.
apiVersion: batch/v1
kind: Job
metadata:
name: dependency-preflight
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-5"
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation,HookSucceeded
spec:
backoffLimit: 0
activeDeadlineSeconds: 60
template:
spec:
restartPolicy: Never
automountServiceAccountToken: false
containers:
- name: check
image: curlimages/curl:8.22.0
command: ["curl"]
args: ["--fail", "--show-error", "--silent", "--connect-timeout", "5", "--max-time", "20", "http://existing-data-service:8080/health"]
---
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/sync-wave: "-3"
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation,HookSucceeded
spec:
backoffLimit: 0
activeDeadlineSeconds: 300
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: myapp/migrations:v1.0.0
command: ["./migrate.sh"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: existing-db-credentials
key: urlA Job that only prints pg_dump to stdout is not a restorable backup procedure. Use persistent encrypted storage, completion verification and restore testing. Validate migration idempotence/locking/recovery; PostSync failure is not automatic rollback.
Minimal Working Ordering Example
This minimal example orders Namespace → ConfigMap → Service → a Deployment with readiness within the same Sync phase. A Healthy Service object does not prove ready endpoints; the Deployment probe checks workload readiness. Prepare Argo Project/RBAC and image-registry access.
apiVersion: v1
kind: Namespace
metadata:
name: wave-demo
annotations:
argocd.argoproj.io/sync-wave: "-2"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wave-demo-config
namespace: wave-demo
annotations:
argocd.argoproj.io/sync-wave: "-1"
data:
DEMO_ENVIRONMENT: demo
---
apiVersion: v1
kind: Service
metadata:
name: wave-demo
namespace: wave-demo
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
type: ClusterIP
selector:
app: wave-demo
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wave-demo
namespace: wave-demo
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
replicas: 2
selector:
matchLabels:
app: wave-demo
template:
metadata:
labels:
app: wave-demo
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: podinfo
image: ghcr.io/stefanprodan/podinfo:6.15.0
ports:
- name: http
containerPort: 9898
envFrom:
- configMapRef:
name: wave-demo-config
readinessProbe:
httpGet:
path: /readyz
port: http
resources:
requests: {cpu: 100m, memory: 64Mi}
limits: {cpu: 500m, memory: 128Mi}
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]For a database, prepare authentication, persistence, Service and actual readiness first. HPA needs CPU requests and metrics availability. Do not put a Service/ConfigMap required for readiness in a later wave.
Sync Windows
This example allows production project prod-* applications on Sunday 02:00–06:00 KST, with a 03:00–04:00 freeze. Replace repository/destination with the actual authorized scope.
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
sourceRepos:
- https://github.com/myorg/myapp.git
destinations:
- server: https://kubernetes.default.svc
namespace: production
syncWindows:
- kind: allow
description: Example Sunday maintenance window
schedule: '0 2 * * 0'
duration: 4h
timeZone: Asia/Seoul
applications: ['prod-*']
namespaces: [production]
andOperator: true
manualSync: false
syncOverrun: false
- kind: deny
description: Example freeze within the maintenance window
schedule: '0 3 * * 0'
duration: 1h
timeZone: Asia/Seoul
applications: ['prod-*']
namespaces: [production]
andOperator: true
manualSync: false
syncOverrun: falseFor a new automated sync request:
- No matching window means no window restriction.
- An active matching deny blocks it.
- Otherwise an active allow permits it.
- Matching allow windows that are all inactive block it.
- With no allow window and only inactive denies, it is permitted.
Application/namespace/cluster selectors default to OR; there is no specificity ranking. Use andOperator: true when the supplied selectors must all match. The default timezone is UTC; a KST comment does not change it.
Manual exceptions depend on all relevant blocking windows permitting manualSync and the caller having sync rights. --force is not a bypass. An always-active 24h allow is not a manually activated emergency switch and can permit automated sync too. Continued execution across a window boundary depends on syncOverrun, start time and the relevant windows; it does not guarantee completion or rollback.
argocd proj windows list production -o yaml
argocd app get my-appDiffing Customization
Ignore only actual fields owned by another controller, scoped by name/namespace. For example, exclude one HPA-managed Deployment's replicas. Do not default to broad image, all-annotation/resources or all-manager exclusions.
spec:
ignoreDifferences:
- group: apps
kind: Deployment
name: my-deployment
namespace: production
jsonPointers:
- /spec/replicas
syncPolicy:
syncOptions: [RespectIgnoreDifferences=true]ignoreDifferences controls comparison; RespectIgnoreDifferences extends it to synchronization, but initial creation without a live object still uses the desired manifest. Inspect actual managed fields before selecting a manager. Global/status comparison exclusions do not disable health evaluation and should not hide image tampering.
See Application ignore-difference examples for scoped rules.
Retry Policies
limit: 5 permits five retries after the initial attempt, up to six attempts total. Delays start at 5s,10s,20s,40s,80s; maxDuration caps an individual backoff, not the entire sync or hook. Application retry and Job backoffLimit/activeDeadlineSeconds are different layers.
Configure automatic retry on sync failures.
Basic Retry Configuration
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/myrepo.git
targetRevision: main
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
retry:
limit: 5 # Maximum retry attempts
backoff:
duration: 5s # Initial delay
factor: 2 # Multiplier for each retry
maxDuration: 3m # Maximum delayRetry Flow
limit: 5 permits up to five retries after the initial attempt. With duration 5s and factor 2, delays begin 5s, 10s, 20s, 40s, 80s. maxDuration caps an individual backoff delay; it is not an overall sync/hook timeout.
Bounded Dependency Preflight
A preflight hook can fail with a bounded deadline; it does not add an Argo retry-by-error-code filter. The dependency below must already exist.
apiVersion: batch/v1
kind: Job
metadata:
name: check-prerequisites
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation,HookSucceeded
spec:
backoffLimit: 0
activeDeadlineSeconds: 100
template:
spec:
restartPolicy: Never
automountServiceAccountToken: false
containers:
- name: check
image: curlimages/curl:8.22.0
command:
- sh
- -c
- |
for i in 1 2 3 4 5 6; do
if curl --fail --silent --show-error --connect-timeout 3 --max-time 10 http://existing-service:8080/readyz; then
exit 0
fi
sleep 5
done
exit 1Selective Sync
Explicit --resource/--label selection skips hooks and history. ApplyOutOfSyncOnly/--apply-out-of-sync-only keeps hooks/history in 3.5.4. --label selects resources; --selector(-l) selects Applications.
Sync only specific resources within an application.
Via CLI
# Sync specific resource by kind and name
argocd app sync my-app --resource 'apps:Deployment:my-deployment'
# Sync resources by group
argocd app sync my-app --resource 'apps:Deployment:*'
# Sync multiple resources
argocd app sync my-app \
--resource ':ConfigMap:my-config' \
--resource ':Secret:my-secret' \
--resource 'apps:Deployment:my-deployment'
# Sync by label
argocd app sync my-app --label 'app.kubernetes.io/component=backend'Sync Options for Selective Sync
# Apply only out-of-sync resources
argocd app sync my-app --apply-out-of-sync-only
# Preview what would be synced
argocd app sync my-app --dry-run
# Sync with prune
argocd app sync my-app --pruneResource Path Format
<group>:<kind>:<name>
Examples:
:ConfigMap:my-config # Core API group
apps:Deployment:my-deployment # apps API group
networking.k8s.io:Ingress:my-ingress # networking.k8s.io group
*:*:* # All resourcesQuiz
To test what you've learned, try the ArgoCD sync strategies quiz.



