Configure schedules for pipelines
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
Azure Pipelines provides several types of triggers to configure how your pipeline starts.
- Scheduled triggers start your pipeline based on a schedule, such as a nightly build. This article provides guidance on using scheduled triggers to run your pipelines based on a schedule.
- Event-based triggers start your pipeline in response to events, such as creating a pull request or pushing to a branch. For information on using event-based triggers, see Triggers in Azure Pipelines.
You can combine scheduled and event-based triggers in your pipelines, for example to validate the build every time a push is made (CI trigger), when a pull request is made (PR trigger), and a nightly build (Scheduled trigger). If you want to build your pipeline only on a schedule, and not in response to event-based triggers, ensure that your pipeline doesn't have any other triggers enabled. For example, YAML pipelines in a GitHub repository have CI triggers and PR triggers enabled by default. For information on disabling default triggers, see Triggers in Azure Pipelines and navigate to the section that covers your repository type.
Scheduled triggers
Important
Scheduled triggers defined using the pipeline settings UI take precedence over YAML scheduled triggers.
If your YAML pipeline has both YAML scheduled triggers and UI defined scheduled triggers, only the UI defined scheduled triggers are run. To run the YAML defined scheduled triggers in your YAML pipeline, you must remove the scheduled triggers defined in the pipeline settings UI. Once all UI scheduled triggers are removed, a push must be made in order for the YAML scheduled triggers to start being evaluated.
To delete UI scheduled triggers from a YAML pipeline, see UI settings override YAML scheduled triggers.
Scheduled triggers configure a pipeline to run on a schedule defined using cron syntax.
schedules:
- cron: string # cron syntax defining a schedule
displayName: string # friendly name given to a specific schedule
branches:
include: [ string ] # which branches the schedule applies to
exclude: [ string ] # which branches to exclude from the schedule
always: boolean # whether to always run the pipeline or only if there have been source code changes since the last successful scheduled run. The default is false.
schedules:
- cron: string # cron syntax defining a schedule
displayName: string # friendly name given to a specific schedule
branches:
include: [ string ] # which branches the schedule applies to
exclude: [ string ] # which branches to exclude from the schedule
always: boolean # whether to always run the pipeline or only if there have been source code changes since the last successful scheduled run. The default is false.
batch: boolean # Whether to run the pipeline if the previously scheduled run is in-progress; the default is false.
# batch is available in Azure DevOps Server 2022.1 and higher
Scheduled pipelines in YAML have the following constraints.
- The time zone for cron schedules is UTC.
- If you specify an
exclude
clause without aninclude
clause forbranches
, it's equivalent to specifying*
in theinclude
clause. - You can't use pipeline variables when specifying schedules.
- If you use templates in your YAML file, then the schedules must be specified in the main YAML file and not in the template files.
Branch considerations for scheduled triggers
Scheduled triggers are evaluated for a branch when the following events occur.
- A pipeline is created.
- A pipeline's YAML file is updated, either from a push, or by editing it in the pipeline editor.
- A pipeline's YAML file path is updated to reference a different YAML file. This change only updates the default branch, and therefore only picks up schedules in the updated YAML file for the default branch. If any other branches subsequently merge the default branch, for example
git pull origin main
, the scheduled triggers from the newly referenced YAML file are evaluated for that branch. - A new branch is created.
After one of these events occurs in a branch, any scheduled runs for that branch are added, if that branch matches the branch filters for the scheduled triggers contained in the YAML file in that branch.
Important
Scheduled runs for a branch are added only if the branch matches the branch filters for the scheduled triggers in the YAML file in that particular branch.
For example, a pipeline is created with the following schedule, and this version of the YAML file is checked into the main
branch. This schedule builds the main
branch on a daily basis.
# YAML file in the main branch
schedules:
- cron: '0 0 * * *'
displayName: Daily midnight build
branches:
include:
- main
Next, a new branch is created based off of main
, named new-feature
. The scheduled triggers from the YAML file in the new branch are read, and since there's no match for the new-feature
branch, no changes are made to the scheduled builds, and the new-feature
branch isn't built using a scheduled trigger.
If new-feature
is added to the branches
list and this change is pushed to the new-feature
branch, the YAML file is read, and since new-feature
is now in the branches list, a scheduled build is added for the new-feature
branch.
# YAML file in the new-feature-branch
schedules:
- cron: '0 0 * * *'
displayName: Daily midnight build
branches:
include:
- main
- new-feature
Now consider that a branch named release
is created based off main
, and then release
is added to the branch filters in the YAML file in the main
branch, but not in the newly created release
branch.
# YAML file in the release branch
schedules:
- cron: '0 0 * * *'
displayName: Daily midnight build
branches:
include:
- main
# YAML file in the main branch with release added to the branches list
schedules:
- cron: '0 0 * * *'
displayName: Daily midnight build
branches:
include:
- main
- release
Because release
was added to the branch filters in the main
branch, but not to the branch filters in the release
branch, the release
branch won't be built on that schedule. Only when the release
branch is added to the branch filters in the YAML file in the release branch will the scheduled build be added to the scheduler.
Batch considerations for scheduled triggers
Note
The batch
property is available on Azure DevOps Server 2022.1 and higher.
The batch
property configures whether to run the pipeline if the previously scheduled run is in-progress; the default is false
. This is regardless of the version of the pipeline repository.
The following table describes how always
and batch
interact.
Always | Batch | Behavior |
---|---|---|
false |
false |
Pipeline runs only if there's a change with respect to the last successful scheduled pipeline run. |
false |
true |
Pipeline runs only if there's a change with respect to the last successful scheduled pipeline run, and there's no in-progress scheduled pipeline run. |
true |
false |
Pipeline runs according to the cron schedule. |
true |
true |
Pipeline runs according to the cron schedule. |
Important
When always
is true
, the pipeline runs according to the cron schedule, even when batch
is true
.
Build.CronSchedule.DisplayName variable
Note
The Build.CronSchedule.DisplayName
variable is available on Azure DevOps Server 2022.1 and higher.
When a pipeline is running due to a cron scheduled trigger, the pre-defined Build.CronSchedule.DisplayName
variable contains the displayName
of the cron schedule that triggered the pipeline run.
Your YAML pipeline may contain multiple cron schedules, and you may want your pipeline to run different stages or jobs based on which cron schedule runs. For example, you have a nightly build and a weekly build, and you want to run a certain stage only during the nightly build. You can use the Build.CronSchedule.DisplayName
variable in a job or stage condition to determine whether to run that job or stage.
- stage: stage1
# Run this stage only when the pipeline is triggered by the
# "Daily midnight build" cron schedule
condition: eq(variables['Build.CronSchedule.DisplayName'], 'Daily midnight build')
For more examples, see schedules.cron examples.
Scheduled builds aren't supported in YAML syntax in Azure DevOps Server 2019. After you create your YAML build pipeline, you can use pipeline settings to specify a scheduled trigger.
Examples
The following example defines two schedules:
schedules:
- cron: '0 0 * * *'
displayName: Daily midnight build
branches:
include:
- main
- releases/*
exclude:
- releases/ancient/*
- cron: '0 12 * * 0'
displayName: Weekly Sunday build
branches:
include:
- releases/*
always: true
The first schedule, Daily midnight build, runs a pipeline at midnight every day, but only if the code has changed since the last successful scheduled run, for main
and all releases/*
branches, except the branches under releases/ancient/*
.
The second schedule, Weekly Sunday build, runs a pipeline at noon on Sundays, whether the code has changed or not since the last run, for all releases/*
branches.
Note
The time zone for cron schedules is UTC, so in these examples, the midnight build and the noon build are at midnight and noon in UTC.
For more examples, see Migrating from the classic editor.
Scheduled builds aren't supported in YAML syntax in Azure DevOps Server 2019. After you create your YAML build pipeline, you can use pipeline settings to specify a scheduled trigger.
Cron syntax
Each Azure Pipelines scheduled trigger cron expression is a space-delimited expression with five entries in the following order. The expression is enclosed in single quotes '
.
mm HH DD MM DW
\ \ \ \ \__ Days of week
\ \ \ \____ Months
\ \ \______ Days
\ \________ Hours
\__________ Minutes
Field | Accepted values |
---|---|
Minutes | 0 through 59 |
Hours | 0 through 23 |
Days | 1 through 31 |
Months | 1 through 12, full English names, first three letters of English names |
Days of week | 0 through 6 (starting with Sunday), full English names, first three letters of English names |
Values can be in the following formats.
Format | Example | Description |
---|---|---|
Wildcard | * |
Matches all values for this field |
Single value | 5 |
Specifies a single value for this field |
Comma delimited | 3,5,6 |
Specifies multiple values for this field. Multiple formats can be combined, like 1,3-6 |
Ranges | 1-3 |
The inclusive range of values for this field |
Intervals | */4 or 1-5/2 |
Intervals to match for this field, such as every fourth value or the range 1-5 with a step interval of 2 |
Example | Cron expression |
---|---|
Build every Monday, Wednesday, and Friday at 6:00 PM | 0 18 * * Mon,Wed,Fri , 0 18 * * 1,3,5 , or 0 18 * * 1-5/2 |
Build every 6 hours | 0 0,6,12,18 * * * , 0 */6 * * * or 0 0-18/6 * * * |
Build every 6 hours starting at 9:00 AM | 0 9,15,21 * * * or 0 9-21/6 * * * |
For more information on supported formats, see Crontab Expression.
Scheduled builds aren't supported in YAML syntax in Azure DevOps Server 2019. After you create your YAML build pipeline, you can use pipeline settings to specify a scheduled trigger.
Scheduled runs view
You can view a preview of upcoming scheduled builds by choosing Scheduled runs from the context menu on the pipeline details page for your pipeline.
Important
The scheduled runs view only shows pipelines scheduled to run within seven days from the current date. If your cron schedule has an interval longer than 7 days and the next run is scheduled to start after seven days from the current date, it won't be displayed in the Scheduled runs view.
After you create or update your scheduled triggers, you can verify them using Scheduled runs view.
This example displays the scheduled runs for the following schedule.
schedules:
- cron: '0 0 * * *'
displayName: Daily midnight build
branches:
include:
- main
The Scheduled runs windows displays the times converted to the local time zone set on the computer used to browse to the Azure DevOps portal. This example displays a screenshot taken in the EST time zone.
Note
If you update the schedule for a running pipeline, the Scheduled runs view isn't updated with the new schedule until the currently running pipeline completes.
Scheduled builds aren't supported in YAML syntax in Azure DevOps Server 2019. After you create your YAML build pipeline, you can use pipeline settings to specify a scheduled trigger.
Running even when there are no code changes
By default, your pipeline doesn't run as scheduled if there have been no code changes since the last successful scheduled run. For instance, consider that you've scheduled a pipeline to run every night at 9:00pm. During the weekdays, you push various changes to your code. The pipeline runs as per schedule. During the weekends, you don't make any changes to your code. If there have been no code changes since the scheduled run on Friday, then the pipeline doesn't run as scheduled during the weekend.
To force a pipeline to run even when there are no code changes, you can use the always
keyword.
schedules:
- cron: ...
...
always: true
Scheduled builds aren't supported in YAML syntax in this version of Azure DevOps Server. After you create your YAML build pipeline, you can use pipeline settings to specify a scheduled trigger.
Limits on the number of scheduled runs in YAML pipelines
There are certain limits on how often you can schedule a pipeline to run. These limits have been put in place to prevent misuse of Azure Pipelines resources, particularly the Microsoft-hosted agents. The limits are:
- around 1000 runs per pipeline per week
- 10 runs per pipeline per 15 minutes
Migrating from the classic editor
The following examples show you how to migrate your schedules from the classic editor to YAML.
- Example: Nightly build of Git repo in multiple time zones
- Example: Nightly build with different frequencies
Example: Nightly build of Git repo in multiple time zones
In this example, the classic editor scheduled trigger has two entries, producing the following builds.
Every Monday - Friday at 3:00 AM (UTC + 5:30 time zone), build branches that meet the
features/india/*
branch filter criteriaEvery Monday - Friday at 3:00 AM (UTC - 5:00 time zone), build branches that meet the
features/nc/*
branch filter criteria
The equivalent YAML scheduled trigger is:
schedules:
- cron: '30 21 * * Sun-Thu'
displayName: M-F 3:00 AM (UTC + 5:30) India daily build
branches:
include:
- /features/india/*
- cron: '0 8 * * Mon-Fri'
displayName: M-F 3:00 AM (UTC - 5) NC daily build
branches:
include:
- /features/nc/*
In the first schedule, M-F 3:00 AM (UTC + 5:30) India daily build, the cron syntax (mm HH DD MM DW
) is 30 21 * * Sun-Thu
.
- Minutes and Hours -
30 21
- This maps to21:30 UTC
(9:30 PM UTC
). Since the specified time zone in the classic editor is UTC + 5:30, we need to subtract 5 hours and 30 minutes from the desired build time of 3:00 AM to arrive at the desired UTC time to specify for the YAML trigger. - Days and Months are specified as wildcards since this schedule doesn't specify to run only on certain days of the month or on a specific month.
- Days of the week -
Sun-Thu
- because of the timezone conversion, for our builds to run at 3:00 AM in the UTC + 5:30 India time zone, we need to specify starting them the previous day in UTC time. We could also specify the days of the week as0-4
or0,1,2,3,4
.
In the second schedule, M-F 3:00 AM (UTC - 5) NC daily build, the cron syntax is 0 8 * * Mon-Fri
.
- Minutes and Hours -
0 8
- This maps to8:00 AM UTC
. Since the specified time zone in the classic editor is UTC - 5:00, we need to add 5 hours from the desired build time of 3:00 AM to arrive at the desired UTC time to specify for the YAML trigger. - Days and Months are specified as wildcards since this schedule doesn't specify to run only on certain days of the month or on a specific month.
- Days of the week -
Mon-Fri
- Because our timezone conversions don't span multiple days of the week for our desired schedule, we don't need to do any conversion here. We could also specify the days of the week as1-5
or1,2,3,4,5
.
Important
The UTC time zones in YAML scheduled triggers don't account for daylight saving time.
Tip
When using 3 letter days of the week and wanting a span of multiple days through Sun, Sun should be considered the first day of the week e.g. For a schedule of midnight EST, Thursday to Sunday, the cron syntax is 0 5 * * Sun,Thu-Sat
.
Example: Nightly build with different frequencies
In this example, the classic editor scheduled trigger has two entries, producing the following builds.
Every Monday - Friday at 3:00 AM UTC, build branches that meet the
main
andreleases/*
branch filter criteriaEvery Sunday at 3:00 AM UTC, build the
releases/lastversion
branch, even if the source or pipeline hasn't changed
The equivalent YAML scheduled trigger is:
schedules:
- cron: '0 3 * * Mon-Fri'
displayName: M-F 3:00 AM (UTC) daily build
branches:
include:
- main
- /releases/*
- cron: '0 3 * * Sun'
displayName: Sunday 3:00 AM (UTC) weekly latest version build
branches:
include:
- /releases/lastversion
always: true
In the first schedule, M-F 3:00 AM (UTC) daily build, the cron syntax is 0 3 * * Mon-Fri
.
- Minutes and Hours -
0 3
- This maps to3:00 AM UTC
. Since the specified time zone in the classic editor is UTC, we don't need to do any time zone conversions. - Days and Months are specified as wildcards since this schedule doesn't specify to run only on certain days of the month or on a specific month.
- Days of the week -
Mon-Fri
- because there's no timezone conversion, the days of the week map directly from the classic editor schedule. We could also specify the days of the week as1,2,3,4,5
.
In the second schedule, Sunday 3:00 AM (UTC) weekly latest version build, the cron syntax is 0 3 * * Sun
.
- Minutes and Hours -
0 3
- This maps to3:00 AM UTC
. Since the specified time zone in the classic editor is UTC, we don't need to do any time zone conversions. - Days and Months are specified as wildcards since this schedule doesn't specify to run only on certain days of the month or on a specific month.
- Days of the week -
Sun
- Because our timezone conversions don't span multiple days of the week for our desired schedule, we don't need to do any conversion here. We could also specify the days of the week as0
. - We also specify
always: true
since this build is scheduled to run whether or not the source code has been updated.
FAQ
- I want my pipeline to run only on the schedule and not when someone pushes a change to a branch
- I defined a schedule in the YAML file. But it didn't run. What happened?
- My YAML schedules were working fine. But, they stopped working now. How do I debug this?
- My code hasn't changed, yet a scheduled build is triggered. Why?
- I see the planned run in the Scheduled runs panel. However, it doesn't run at that time. Why?
- Schedules defined in YAML pipeline work for one branch but not the other. How do I fix this?
I want my pipeline to run only on the schedule and not when someone pushes a change to a branch
If you want your pipeline to run only on the schedule, and not when someone pushes a change to a branch or merges a change to the main branch, you must explicitly disabled the default CI and PR triggers on the pipeline.
To disable the default CI and PR triggers, add the following statements to your YAML pipeline, and verify that you haven't overridden the YAML pipeline triggers with UI triggers.
trigger: none
pr: none
For more information, see pr definition and trigger definition.
I defined a schedule in the YAML file. But it didn't run. What happened?
Check the next few runs that Azure Pipelines has scheduled for your pipeline. You can find these runs by selecting the Scheduled runs action in your pipeline. The list is filtered down to only show you the upcoming few runs over the next few days. If this doesn't meet your expectation, it's probably the case that you've mistyped your cron schedule, or you don't have the schedule defined in the correct branch. Read the topic above to understand how to configure schedules. Reevaluate your cron syntax. All the times for cron schedules are in UTC.
Make a small trivial change to your YAML file and push that update into your repository. If there was any problem in reading the schedules from the YAML file earlier, it should be fixed now.
If you have any schedules defined in the UI, then your YAML schedules aren't honored. Ensure that you don't have any UI schedules by navigating to the editor for your pipeline and then selecting Triggers.
There's a limit on the number of runs you can schedule for a pipeline. Read more about limits.
If there are no changes to your code, they Azure Pipelines may not start new runs. Learn how to override this behavior.
My YAML schedules were working fine. But, they stopped working now. How do I debug this?
If you didn't specify
always:true
, your pipeline won't be scheduled unless there are any updates made to your code. Check whether there have been any code changes and how you configured the schedules.There's a limit on how many times you can schedule your pipeline. Check if you've exceeded those limits.
Check if someone enabled more schedules in the UI. Open the editor for your pipeline, and select Triggers. If they defined schedules in the UI, then your YAML schedules won't be honored.
Check if your pipeline is paused or disabled. Select Settings for your pipeline.
Check the next few runs that Azure Pipelines has scheduled for your pipeline. You can find these runs by selecting the Scheduled runs action in your pipeline. If you don't see the schedules that you expected, make a small trivial change to your YAML file, and push the update to your repository. This should resync the schedules.
If you use GitHub for storing your code, it's possible that Azure Pipelines may have been throttled by GitHub when it tried to start a new run. Check if you can start a new run manually.
My code hasn't changed, yet a scheduled build is triggered. Why?
You might have enabled an option to always run a scheduled build even if there are no code changes. If you use a YAML file, verify the syntax for the schedule in the YAML file. If you use classic pipelines, verify if you checked this option in the scheduled triggers.
You might have updated the build pipeline or some property of the pipeline. This will cause a new run to be scheduled even if you haven't updated your source code. Verify the History of changes in the pipeline using the classic editor.
You might have updated the service connection used to connect to the repository. This will cause a new run to be scheduled even if you haven't updated your source code.
Azure Pipelines first checks if there are any updates to your code. If Azure Pipelines is unable to reach your repository or get this information, it will create an informational run. It's a dummy build to let you know that Azure Pipelines is unable to reach your repository.
Your pipeline may not have a completely successful build. In order to determine whether to schedule a new build or not, Azure DevOps looks up the last completely successful scheduled build. If it doesn't find one, it triggers a new scheduled build. Partially successful scheduled builds aren't considered successful, so if your pipeline only has partially successful builds, Azure DevOps will trigger scheduled builds, even if your code hasn't changed.
I see the planned run in the Scheduled runs panel. However, it doesn't run at that time. Why?
- The Scheduled runs panel shows all potential schedules. However, it may not actually run unless you have made real updates to the code. To force a schedule to always run, ensure that you have set the always property in the YAML pipeline, or checked the option to always run in a classic pipeline.
Schedules defined in YAML pipeline work for one branch but not the other. How do I fix this?
Schedules are defined in YAML files, and these files are associated with branches. If you want a pipeline to be scheduled for a particular branch, say features/X
, then make sure that the YAML file in that branch has the cron schedule defined in it, and that it has the correct branch inclusions for the schedule. The YAML file in the features/X
branch should have the following schedule
in this example:
schedules:
- cron: '0 12 * * 0' # replace with your schedule
branches:
include:
- features/X
For more information, see Branch considerations for scheduled triggers.