ArgoCD Sync Policies

Sync policies control when and how ArgoCD synchronizes applications. They determine whether syncs happen automatically or manually, what happens during sync, and when syncs are allowed or blocked.

Sync Policy Overview

A sync policy defines:

  • When to sync - Automated or manual
  • What to sync - Which resources and options
  • How to sync - Sync strategy and hooks
  • When syncs are allowed - Sync windows
graph TB A[Application] --> B{Sync Policy} B --> C[Automated] B --> D[Manual] C --> E[Auto Sync] C --> F[Prune] C --> G[Self Heal] D --> H[Manual Trigger] I[Sync Windows] --> B J[Sync Hooks] --> B K[Sync Options] --> B style B fill:#e1f5ff style C fill:#e8f5e9 style D fill:#fff4e1

Automated Sync

Automated sync automatically applies changes from Git to the cluster when differences are detected.

Basic Automated Sync

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-app
  namespace: argocd
spec:
  syncPolicy:
    automated:
      prune: true      # Delete resources removed from Git
      selfHeal: true   # Automatically sync on cluster drift

Automated Sync Options

  • prune - Automatically delete resources that exist in cluster but not in Git
  • selfHeal - Automatically sync when cluster state drifts from Git
  • allowEmpty - Allow applications with no resources (default: false)

Example: Full Automated Sync

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-app
  namespace: argocd
spec:
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
    - CreateNamespace=true
    - PruneLast=true

Manual Sync

Manual sync requires explicit user action to apply changes.

Manual Sync Configuration

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-app
  namespace: argocd
spec:
  syncPolicy:
    # No automated section = manual sync
    syncOptions:
    - CreateNamespace=true

Triggering Manual Sync

# Sync application
argocd app sync production-app

# Sync with specific revision
argocd app sync production-app --revision v1.2.3

# Sync with prune
argocd app sync production-app --prune

# Sync with force (bypass hooks)
argocd app sync production-app --force

Sync Options

Sync options control how resources are synchronized.

Common Sync Options

  • CreateNamespace=true - Create namespace if it doesn’t exist
  • PruneLast=true - Prune resources after all other resources are synced
  • PrunePropagationPolicy=foreground - Prune with foreground deletion
  • RespectIgnoreDifferences=true - Respect ignore differences settings
  • ApplyOutOfSyncOnly=true - Only sync out-of-sync resources

Example with Sync Options

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-app
  namespace: argocd
spec:
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true
    - PruneLast=true
    - PrunePropagationPolicy=foreground
    - RespectIgnoreDifferences=true

Sync Strategies

Sync strategies define the order and method of applying resources.

Hook-Based Sync

Use hooks to run operations before, during, or after sync.

sequenceDiagram participant App as Application participant Hook as PreSync Hook participant K8s as Kubernetes participant PostHook as PostSync Hook App->>Hook: 1. Execute PreSync Hook Hook->>K8s: Run PreSync Job K8s->>Hook: Job Complete Hook->>App: PreSync Success App->>K8s: 2. Sync Resources K8s->>App: Resources Applied App->>PostHook: 3. Execute PostSync Hook PostHook->>K8s: Run PostSync Job K8s->>PostHook: Job Complete PostHook->>App: PostSync Success

Sync Waves

Control the order of resource synchronization using annotations:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: database
  annotations:
    argocd.argoproj.io/sync-wave: "1"  # Sync first
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  annotations:
    argocd.argoproj.io/sync-wave: "2"  # Sync after database
---
apiVersion: v1
kind: Service
metadata:
  name: api
  annotations:
    argocd.argoproj.io/sync-wave: "3"  # Sync last

Lower wave numbers sync first. Default wave is 0.

Sync Wave Example

graph TB A[Wave -1: Namespace] --> B[Wave 0: ConfigMap/Secret] B --> C[Wave 1: Database] C --> D[Wave 2: API] D --> E[Wave 3: Service] style A fill:#e1f5ff style B fill:#e8f5e9 style C fill:#fff4e1 style D fill:#f3e5f5 style E fill:#ffe1e1

Sync Hooks

Hooks are Kubernetes resources that run at specific points during sync.

Hook Types

  1. PreSync - Runs before sync
  2. Sync - Runs during sync (replaces normal sync)
  3. PostSync - Runs after sync
  4. SyncFail - Runs if sync fails

PreSync Hook Example

Run database migrations before deploying application:

apiVersion: batch/v1
kind: Job
metadata:
  name: database-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: migrate
        image: myapp:migrate
        command: ["/bin/sh", "-c", "python manage.py migrate"]
      restartPolicy: Never
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  # No hook annotation = normal resource

PostSync Hook Example

Run smoke tests after deployment:

apiVersion: batch/v1
kind: Job
metadata:
  name: smoke-tests
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: test
        image: myapp:test
        command: ["/bin/sh", "-c", "npm test"]
      restartPolicy: Never

Hook Delete Policies

Control when hooks are deleted:

  • HookSucceeded - Delete when hook succeeds
  • HookFailed - Delete when hook fails
  • BeforeHookCreation - Delete previous hook before creating new one
apiVersion: batch/v1
kind: Job
metadata:
  name: pre-sync-job
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  # ...

Complete Hook Example

# PreSync: Backup database
apiVersion: batch/v1
kind: Job
metadata:
  name: backup-db
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: backup
        image: postgres:14
        command: ["pg_dump", "-h", "db", "-U", "user", "mydb"]
      restartPolicy: Never
---
# Normal resource: Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  # ...
---
# PostSync: Health check
apiVersion: batch/v1
kind: Job
metadata:
  name: health-check
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: check
        image: curlimages/curl
        command: ["curl", "-f", "http://web-app/health"]
      restartPolicy: Never

Sync Windows

Sync windows define when Applications can be synced. Useful for:

  • Preventing deployments during business hours
  • Maintenance windows
  • Change freeze periods

Application-Level Sync Windows

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-app
  namespace: argocd
spec:
  syncPolicy:
    syncWindows:
    - kind: allow
      schedule: '0 2 * * *'  # 2 AM daily
      duration: 1h
      manualSync: true
    - kind: deny
      schedule: '* * * * *'  # All other times

Project-Level Sync Windows

Define sync windows at the Project level:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: production
  namespace: argocd
spec:
  syncWindows:
  - kind: allow
    schedule: '0 2 * * 1-5'  # Weekdays 2 AM
    duration: 2h
    applications:
    - 'production-*'
    manualSync: true
  - kind: deny
    schedule: '* * * * *'    # All other times
    applications:
    - 'production-*'

Sync Window Schedule Format

Uses cron syntax: minute hour day month weekday

Examples:

  • 0 2 * * * - Daily at 2 AM
  • 0 2 * * 1-5 - Weekdays at 2 AM
  • 0 */4 * * * - Every 4 hours
  • 0 9-17 * * 1-5 - Business hours on weekdays

Sync Window Properties

  • kind - allow or deny
  • schedule - Cron expression
  • duration - How long the window lasts (e.g., 1h, 30m)
  • applications - Which applications (supports wildcards)
  • namespaces - Which namespaces (supports wildcards)
  • clusters - Which clusters (supports wildcards)
  • manualSync - Allow manual sync during deny windows

Example: Business Hours Restriction

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: production
  namespace: argocd
spec:
  syncWindows:
  # Allow syncs during maintenance window
  - kind: allow
    schedule: '0 2 * * 1-5'  # Weekdays 2-4 AM
    duration: 2h
    applications:
    - 'production-*'
    manualSync: true
  # Deny all other times
  - kind: deny
    schedule: '* * * * *'
    applications:
    - 'production-*'
    manualSync: false  # No manual syncs outside window

Sync Status and Conditions

Monitor sync status and conditions:

# Get sync status
argocd app get web-app

# View sync operation details
argocd app get web-app --show-operation

# Check sync history
argocd app history web-app

Sync Conditions

Applications have sync conditions:

  • Synced - Application is in sync
  • Healthy - All resources are healthy
  • Progressing - Sync is in progress
  • Degraded - Some resources are unhealthy
  • Suspended - Sync is paused

Sync Conflicts

Handling Conflicts

When cluster state conflicts with Git:

# View differences
argocd app diff web-app

# Force sync (overwrites cluster changes)
argocd app sync web-app --force

# Use replace strategy
argocd app sync web-app --strategy replace

Ignore Differences

Ignore specific fields that change in cluster:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-app
  namespace: argocd
spec:
  ignoreDifferences:
  - group: apps
    kind: Deployment
    jsonPointers:
    - /spec/replicas  # Ignore replica count changes
  - group: v1
    kind: Service
    jsonPointers:
    - /spec/clusterIP  # Ignore cluster IP changes

Best Practices

1. Use Automated Sync for Development

spec:
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

2. Use Manual Sync for Production

spec:
  syncPolicy:
    # No automated section
    syncOptions:
    - CreateNamespace=true

3. Use Sync Windows for Production

spec:
  syncPolicy:
    syncWindows:
    - kind: allow
      schedule: '0 2 * * 1-5'
      duration: 2h
      manualSync: true

4. Use Hooks for Complex Deployments

  • PreSync for migrations, backups
  • PostSync for health checks, notifications
  • Delete policies to clean up hooks

5. Use Sync Waves for Dependencies

Order resources by dependencies using sync waves.

6. Monitor Sync Status

Set up alerts for:

  • Sync failures
  • OutOfSync state
  • Degraded applications

Troubleshooting

Sync Stuck

# Check sync status
argocd app get web-app

# View operation details
argocd app get web-app --show-operation

# Force sync
argocd app sync web-app --force

Hook Failures

# Check hook status
kubectl get jobs -n <NAMESPACE>

# View hook logs
kubectl logs job/<HOOK_NAME> -n <NAMESPACE>

# Retry sync
argocd app sync web-app

Sync Window Issues

# Check active sync windows
argocd app get web-app

# Override sync window (if manualSync enabled)
argocd app sync web-app --skip-sync-window

Summary

Sync policies control how ArgoCD synchronizes applications:

  • Automated sync - Automatic synchronization from Git
  • Manual sync - Requires explicit user action
  • Sync options - Control sync behavior
  • Sync hooks - Run operations at specific points
  • Sync windows - Restrict when syncs can occur
  • Sync waves - Control resource sync order

Configure sync policies based on your environment needs:

  • Development: Automated sync with self-heal
  • Production: Manual sync with sync windows
  • Complex deployments: Use hooks and sync waves