Sync Windows¶
Sync windows control when doco-cd is allowed to change your stacks. For example, you can deploy only during business hours, freeze deployments over the weekend, or restrict production to a nightly maintenance window. Each window is either an allow or a deny window. It opens on a cron schedule and stays active for a fixed duration.
Windows are configured on the doco-cd instance
You set sync windows with SYNC_WINDOWS or SYNC_WINDOWS_FILE on the doco-cd container, not in the .doco-cd.yaml deploy config or in a poll config. That way a commit can't lift the window that is supposed to hold it back.
Configuration¶
| Key | Type | Description | Default |
|---|---|---|---|
SYNC_WINDOWS | list | A YAML list of sync windows (see below). | Ignored when not specified |
SYNC_WINDOWS_FILE | string | Path to a file inside the container that contains the list of sync windows in YAML format. | Ignored when not specified |
SYNC_WINDOWS and SYNC_WINDOWS_FILE are mutually exclusive. If a window is invalid, doco-cd refuses to start. At startup, doco-cd logs every configured window and whether it is currently active.
Each window supports the following settings:
Settings without a default value are required.
| Key | Type | Description | Default |
|---|---|---|---|
name | string | Name of the window, shown in logs, metrics and the API. Names must be unique (case-insensitive). | <kind>-<position> |
kind | string | allow or deny. See evaluation. | |
schedule | string | When the window opens. Use a 5-field cron expression without seconds (minute hour day-of-month month day-of-week) or a predefined schedule such as @daily. @every intervals and TZ=/CRON_TZ= prefixes are not supported (use timezone instead). | |
duration | string | How long the window stays active after each opening, as a Go duration (e.g. 30m, 10h, 62h). Minimum 1m. | |
timezone | string | IANA timezone the schedule is evaluated in, e.g. Europe/Berlin. | Timezone of doco-cd (TZ) |
repositories | list of strings | Glob patterns matched against the repository name, e.g. github.com/acme/*. | All repositories |
deployments | list of strings | Glob patterns matched against the deployment name from the deploy config (the Compose project or Swarm stack name). | All deployments |
contexts | list of strings | Glob patterns matched against the Docker context name. The default context is default. | All contexts |
manual_sync | boolean | Allow manual deployments that this window would block. | false |
Selectors¶
repositories, deployments and contexts select the deployments a window applies to. A window applies to a deployment only if all of its selectors match. An empty or omitted selector matches everything.
- Patterns are matched case-insensitively against the whole value.
*matches any number of characters, including/, and?matches exactly one character.- The repository name is the one doco-cd logs in the
repositoryfield, i.e. host and path without scheme and.gitsuffix, e.g.github.com/acme/app. For deploy configs with arepository_url, a pattern matches if it matches either the repository inrepository_urlor the repository that contains the deploy config.
Timezone and DST¶
Each window's schedule is evaluated in its timezone, or in the timezone of doco-cd (TZ) if none is set. Windows follow daylight saving time changes: a window scheduled at 0 8 * * * in Europe/Berlin always opens at 08:00 local time.
Evaluation¶
A window is active from each time its schedule fires until duration has passed. Occurrences may overlap, for example a daily schedule with a 36h duration.
When doco-cd is about to change a stack, it only considers the windows that match the deployment:
- If no window matches, the deployment is allowed.
- If a matching
denywindow is active, the deployment is blocked. Deny windows always win over allow windows. - If there are matching
allowwindows but none of them is active, the deployment is blocked. - Otherwise, the deployment is allowed.
Only actual changes are checked. A poll or webhook that finds nothing to deploy is never reported as deferred.
The decision is made once, when the webhook, poll or API request arrives. A deployment that was allowed then finishes, even if the window closes while it waits for a free deployment slot (MAX_CONCURRENT_DEPLOYMENTS) or while it runs.
If a single trigger deploys several stacks and only some of them are blocked, the allowed stacks are deployed and the blocked ones are deferred.
What sync windows apply to¶
| Action | Affected by sync windows |
|---|---|
| Deployments triggered by webhooks | Yes |
Deployments triggered by poll jobs (with interval, schedule or the local repository watcher) | Yes |
Poll runs triggered via the REST API or the MCP trigger_poll tool | Yes, unless every blocking window has manual_sync |
| The one-shot self-update bootstrap1 | Yes, unless every blocking window has manual_sync |
destroy: true deploy configs and the removal of auto-discovered stacks that were deleted from the repository | Yes |
| Reconciliation restoring the already deployed revision, e.g. after a container died | No, unless it would deploy another revision, see limitations |
| Scheduled jobs | No |
| Project and stack actions via the REST API or MCP (start, stop, restart, scale, remove, …) | No |
Manual deployments¶
Poll runs you trigger via the REST API or the MCP server count as manual deployments. A manual deployment may bypass a block only if every window that blocks it has manual_sync: true. This lets you do emergency deployments during a freeze while webhooks and polls stay blocked. doco-cd logs sync window bypassed by manual deployment whenever this happens.
Deferred deployments¶
A blocked deployment is skipped, not queued. doco-cd doesn't replay it once the window opens. Instead, the next trigger that arrives while the window is open deploys the then-latest revision:
- Polling: the next poll inside the window picks up the change automatically. This is the recommended setup with sync windows.
- Webhooks only: nothing happens until the next webhook arrives inside a window, e.g. with the next push, or until you trigger a poll run.
Catch up when an allow window opens
Combine an allow window with a poll job whose schedule fires when the window opens, so deferred changes are deployed right away:
- url: https://github.com/acme/app.git
schedule: "0 8-17 * * 1-5" # every hour during business hours, starting when the window opens
Poll schedules use the timezone of doco-cd (TZ). If the window has a different timezone, prefix the poll schedule with it, e.g. CRON_TZ=Europe/Berlin 0 8-17 * * 1-5.
Reporting¶
When a deployment is deferred, doco-cd reports it as follows:
- Logs:
deployment deferred by sync windowwith the blockingsync_windowsand thenext_opentime, if known. It is logged atinfolevel the first time a revision of a stack is deferred and atdebuglevel on repeated polls. Deferred removals of deleted auto-discovered stacks are logged asremoval of obsolete auto-discovered stack deferred by sync window. - Webhooks: if every stack of the webhook was deferred, the response is
202 Acceptedwith the messagedeployment deferred by sync window until <time>. - Deployment runs: runs where every stack was deferred have the status
skippedin the deployment runs API. A poll run triggered withwait=trueresponds with202 Acceptedand the deferral message. The MCPtrigger_polltool returns the statusskipped. - Commit status: with
GIT_COMMIT_STATUSenabled, the commit gets apendingstatusDeferred by sync window until <time>. It becomessuccessonce the same commit is deployed inside a window. - Metrics:
doco_cd_sync_window_blocked_total{repository,deployment,context,window}counts every deferral, once per blocking window, see Prometheus Metrics. - Notifications: deferrals do not send notifications, because they would repeat on every poll.
Inspecting sync windows¶
Use the GET /v1/api/sync-windows endpoint or the MCP tool list_sync_windows to list all windows and whether they are active. With a repository, deployment or context, they also tell you whether that deployment would be allowed right now and when it may deploy next.
Examples¶
Only deploy on weekdays between 08:00 and 18:00 in Berlin:
Block all deployments from Friday 18:00 until Monday 08:00:
Deploy to the production Docker context only at night, while other contexts are unrestricted:
Block the deployments of the acme organization over the holidays, but allow poll runs triggered via the REST API or MCP:
Limitations¶
- No queue: deferred deployments are not replayed when a window opens, see deferred deployments.
- Reconciliation while a revision is deferred: reconciliation keeps restoring the revision that was deployed before, and the stack is not removed as an obsolete auto-discovered stack. If doco-cd restarts while a revision is deferred, it doesn't know the previous revision of that stack anymore, so the stack is not reconciled until a deployment inside a window succeeds.
- Reconciliation after a restart with scheduled poll jobs: reconciliation of a repository starts with its first deployment run after doco-cd started. A poll job with a
scheduledoes not poll at startup, so its stacks are not reconciled until the first scheduled poll. - Reconciliation of stacks with their own reference: reconciliation resolves the reference of deploy configs with their own
reference,repository_urlorgit_depthagain. While a window blocks such a stack, it is only restored if the resolved revision matches the revision its containers are labeled with. If the reference moved on, or none of its containers are left to compare with, reconciliation is deferred like an automatic deployment. - Superseded commits stay pending: if a deferred commit is superseded by a newer one before a window opens, its
pendingcommit status is never updated. - Admitted deployments finish: a deployment that was allowed when its trigger arrived is not cancelled when a window closes.