Block Argo CD Syncs During a Release Freeze
Use sync windows to deny automatic syncs during a freeze while still allowing audited manual syncs for real emergencies.
Task
Prevent Argo CD from rolling out changes during a freeze window without locking engineers out of a genuine incident.
When to use this
A marketing launch runs on Saturday and an unrelated merge to main auto-syncs at 09:00, restarting the API pods during peak traffic.
Prerequisites
- Argo CD 2.9 or newer
- An AppProject that owns the affected Applications
- argocd CLI logged in to the instance
Steps
1. Add a deny window for the freeze and an allow window for normal working days.
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: payments
namespace: argocd
spec:
syncWindows:
- kind: deny
schedule: "0 0 * * 6"
duration: 48h
timeZone: Europe/London
applications:
- "*"
manualSync: true
- kind: allow
schedule: "0 0 * * 1-5"
duration: 120h
timeZone: Europe/London
applications:
- "*"
Expected output: Automatic sync is denied from Saturday 00:00 for 48 hours, while manual sync stays available.
2. Confirm the project windows are what the controller will evaluate.
argocd proj windows list paymentsExpected output: Both windows appear with the correct schedule, duration, time zone, and manual sync flag.
3. Verify that an out-of-sync application is not synced inside the deny window.
argocd app get payments-api -o json | jq -r '.status.sync.status, .status.operationState.phase'Expected output: Sync status is OutOfSync while the operation phase stays empty during the freeze.
4. Perform an emergency sync explicitly and confirm it is attributed to a user.
argocd app sync payments-api --server-side --prune --timeout 300Expected output: The sync runs despite the deny window and appears in the app history with the initiating account.
Troubleshooting
Problem
The application syncs anyway during the freeze
Solution
Sync windows belong to the AppProject that owns the application; check that the app is not still in the default project.
Problem
The window never matches the intended time
Solution
The controller evaluates the window time zone, which is UTC by default; set timeZone explicitly and confirm the controller clock.
Limitations
- Sync windows do not stop direct kubectl changes; pair them with admission policy for a real freeze.
- A deny window with manualSync disabled can block a genuine emergency; keep manual sync enabled and audited.
Freeze automation without freezing people
A release freeze is usually a calendar instruction, and calendar instructions are ignored by automation. Argo CD sync windows move the freeze into the reconciliation loop: inside a deny window the controller stops automatic syncing, so a merge to main cannot restart production pods during a launch. The important detail is manualSync: true. A freeze that also blocks manual sync turns a minor incident into an outage because nobody can apply the fix.
Shape the windows
Windows are evaluated in the order they appear, and the most specific match wins. Keep a deny window for the freeze period and an allow window for normal hours so an expired deny window does not leave the project in an undefined state. Set timeZone explicitly, because the controller defaults to UTC and a Saturday freeze written in local time will start eight hours early or late depending on the season.
syncWindows:
- kind: deny
schedule: "0 0 * * 6"
duration: 48h
timeZone: Europe/London
applications: ["*"]
manualSync: true
Verify and audit the boundary
Check the effective windows with argocd proj windows list rather than trusting the manifest, and confirm an out-of-sync application stays unsynced while the window is active. When an emergency override happens, the app history records the initiating account and the diff, which is exactly what a post-incident review needs. Announce the freeze in the same channel where the override procedure is documented, so nobody has to guess whether manual sync is allowed.