Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXVII · Argo CDSync

Sync policies and sync windows — automated, manual, prune, and the time-bound gate

Advanced⏱ ~28 mingitargocd

What you'll learn

  • Distinguish automated sync from manual sync and identify the operational trade-off of each
  • Configure prune and self-heal independently and recognise the failure mode each prevents
  • Define sync windows that gate automated syncs to a maintenance window
  • Choose the right sync policy for production vs non-production environments

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

The Application’s syncPolicy is the gate between “controller detected a diff” and “controller applied the diff”. Without a sync policy, every Application is manual: the controller surfaces the diff and an operator runs argocd app sync to apply it. With an automated sync policy, the controller applies the diff itself, with sub-options for pruning and self-healing that control how it applies. Sync windows gate automated syncs to specific time bands.

Automated versus manual

The two top-level sync modes:

  • Manual. The controller reconciles, detects the diff, updates status.sync.status to OutOfSync, and waits. The operator reviews and runs argocd app sync NAME to apply. Manual sync is the default and the right choice for high-risk production Applications.
  • Automated. The controller reconciles, detects the diff, and applies it. The syncPolicy.automated block carries sub-options that shape what “applies” means. The right choice for Applications where the diff is well-understood and the cost of an out-of-cycle apply is lower than the cost of waiting.
argocd app set payment-api \
  --sync-policy automated
flowchart LR
    D["Diff detected"] --> P{"syncPolicy"}
    P -->|manual| W["Wait for argocd app sync"]
    P -->|automated| A["Apply diff"]
    W --> O["Operator review"]
    O --> S["argocd app sync"]
    S --> A
    A --> K["Cluster updated"]

The trade-off is control versus latency. Manual sync adds a human in the loop; automated sync removes the human but also removes the latency. A team that runs hundreds of Applications across many clusters cannot review every diff manually; automated sync is the operational answer.

Prune and self-heal

Two sub-options inside syncPolicy.automated control what the controller does when desired and live diverge:

  • Prune. When the rendered source no longer contains a resource that exists in the cluster, the controller deletes it. Pruning is destructive: a typo that removes a resource from the chart deletes it from the cluster. Right for Applications whose resource set is owned end-to-end by the chart.
  • Self-heal. When the live state differs from the desired state for reasons the controller did not cause (a manual kubectl edit, an out-of-band Helm install), the controller reapplies desired state. Right for Applications where the chart is the single source of truth and any drift is a bug.

The two are independent. A team can run automated sync with prune but without self-heal (prunes deleted resources but does not revert out-of-band edits), or with self-heal but without prune (reverts drift but does not delete resources that disappeared from the source).

syncPolicy:
  automated:
    prune: true
    selfHeal: true
argocd app set payment-api \
  --sync-policy automated \
  --auto-prune \
  --self-heal

Sync windows

A sync window is a time band during which automated sync is allowed (or denied). Sync windows are defined at the AppProject level, not per-Application, because the maintenance window is a property of the environment, not of the workload.

  • Allow. During the window, automated sync is permitted. Outside the window, automated sync is blocked; manual sync still works.
  • Deny. During the window, automated sync is blocked. Outside the window, automated sync is permitted.
kind: AppProject
metadata:
  name: payments
spec:
  syncWindows:
    - kind: allow
      schedule: '0 2 * * *'
      duration: 4h
      applications:
        - 'payment-*'
      manualSync: true

The fields and what they own:

  • kindallow or deny.
  • schedule — a cron expression. The window starts at each cron tick.
  • duration — how long the window stays open. A four-hour allow window at 0 2 * * * covers 02:00-06:00 UTC daily.
  • applications — a glob list of Application names. The window applies only to the listed Applications.
  • manualSync — when true, manual sync is also permitted during a deny window. Useful for emergencies.
gantt
    title Sync window - allow 02:00-06:00 UTC daily
    dateFormat  HH:mm
    axisFormat %H:%M
    section Day 1
    Window (allow) :active, 02:00, 4h
    Blocked       :       06:00, 22h
    section Day 2
    Window (allow) :active, 02:00, 4h

A production team that runs scheduled releases uses sync windows to gate automated syncs to a maintenance window. The window is the operational contract: production changes happen at 02:00 UTC and not at any other time unless a human invokes manual sync.

Sync options

The syncOptions field is an array of strings that toggle per-sync behaviour. Options most production teams need:

  • CreateNamespace=true — creates the destination namespace if it does not exist.
  • PrunePropagationPolicy=foreground — controls prune order. Foreground blocks dependents until the owner is deleted.
  • PruneLast=true — applies new resources before deleting pruned ones. Reduces the partial-applied window.
  • ApplyOutOfSyncOnly=true — applies only out-of-sync resources. Faster for large sets with small diffs.
  • ServerSideApply=true — uses server-side apply. The right choice for resources with shared ownership and managedFields.

Under the hood

The controller’s reconcile loop is a state machine with three states: Synced, OutOfSync, Unknown. The transition between OutOfSync and Synced is the apply; the transition between Synced and OutOfSync is the detection. The sync policy decides what happens during the transition out of OutOfSync: with manual sync the controller waits; with automated it applies. The sync window is a gate on the automated transition.

Production discipline

The rules for production sync policies:

  1. Manual sync is the default for production Applications that change state. A manual policy forces a human to invoke argocd app sync, which forces a review of the diff, which forces the change to be intentional.
  2. prune: true is enabled only after a namespace backup. Pruning deletes resources. The first time a chart prunes a resource, the team must be able to restore it.
  3. selfHeal: true is enabled when the chart owns the live state. Self-healing reverts out-of-band edits. A team with legitimate reasons to edit live state (debugging, one-off certificates) must disable self-heal for those resources.
  4. Sync windows are defined per environment, not per workload. The window is the operational contract; the workload inherits it from the project.

Cross-course references

  • Kubernetes for Production Sysadmins - Part XVI (Operators and Controllers) covers the reconcile loop pattern that Argo CD’s sync policy implements.
  • Git, CI/CD & GitOps for Infrastructure Engineers - Part LXXV (Pull-based Deployment) is the operational pattern; Part LXXVI-05 (Promotion Models) is how the sync window fits into a promotion pipeline.

Quiz

Knowledge check · 4 questions

  1. Q1. A production Application uses automated sync with prune enabled. An engineer renames a resource in the chart and commits. What does the controller do?

  2. Q2. A sync window of kind: deny blocks manual sync as well as automated sync by default.

  3. Q3. Name the two sub-options of syncPolicy.automated and identify which one reverts out-of-band kubectl edits.

  4. Q4. Diagnose why the production deploy happened outside the maintenance window and recommend a fix.

    A team has automated sync enabled with a sync window of kind: allow, schedule '0 2 * * *', duration 4h. The controller is observed applying a production change at 14:30 UTC on a Tuesday. The Application's status shows the change was applied as automated sync.

Passing score: 75%. Answers are checked in this browser.