Run Flows on a Cron Schedule with Backfills and Conditions

For the complete documentation index, see llms.txt. For a full content snapshot, see llms-full.txt. Append .md to any kestra.io/docs/* URL for plain Markdown.

Schedule flows using cron expressions.

The Schedule trigger generates new executions on a regular cadence based on a Cron expression or custom scheduling conditions.

type: io.kestra.plugin.core.trigger.Schedule

Kestra can trigger flows on a defined schedule. If you need to wait for another system to be ready and no event mechanism is available, you can configure one or more time-based schedules for your flow.

Kestra can automatically handle backfills to recover missed executions.

Check the Schedule trigger documentation for the list of properties and outputs.

Cron shortcuts

Kestra supports the following cron extensions instead of writing a cron expression:

  • @yearly and @annually - runs yearly on 1st January at 00:00
  • @monthly - runs monthly on the 1st at 00:00
  • @weekly - runs weekly on Sunday at 00:00
  • @daily and @midnight - runs at 00:00 every day
  • @hourly - runs every hour, on the hour

Examples

Schedule that runs every 15 minutes:

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "*/15 * * * *"

Schedule that runs only on the first monday of every month at 11 AM:

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * 1"
when: "{{ isDayWeekInMonth(trigger.date, 'MONDAY', 'FIRST') }}"

A schedule that runs daily at midnight US Eastern time:

triggers:
- id: daily
type: io.kestra.plugin.core.trigger.Schedule
cron: "@daily"
timezone: America/New_York

Schedule that runs on the last day of every month. The L symbol in the day-of-month field represents the last day:

triggers:
- id: month_end
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 12 L * *"

This runs at 12:00 on the last day of every month, including shorter months like February.

Refining schedules with when

When a cron expression alone is not sufficient (e.g., only first Monday of the month, only weekends), you can refine schedules using a when Pebble expression.

You can use the {{ trigger.date }} expression to access the current schedule date within the when expression. The date and calendar helper functions in the expressions reference cover all available date functions such as isDayWeekInMonth(), dayOfWeek(), isWeekend(), isPublicHoliday(), and isLastWorkingDay().

The when expression is evaluated and {{ trigger.previous }} and {{ trigger.next }} reflect the date with the condition applied.

Here’s an example using a day-of-week check:

id: conditions
namespace: company.team
tasks:
- id: hello
type: io.kestra.plugin.core.log.Log
message: This will execute only on Thursday!
triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "@hourly"
when: "{{ dayOfWeek(trigger.date) == 'THURSDAY' }}"

Recover missed schedules

Automatically

By default, Kestra automatically recovers missed schedules. This means that if the Kestra server is down, the missed schedules will be executed as soon as the server is back up. However, this behavior is not always desirable, e.g. during a planned maintenance window. This behavior can be disabled by setting the recoverMissedSchedules configuration to NONE.

Configure recoverMissedSchedules behavior in your global Kestra configuration to choose whether you want to recover missed schedules automatically or not:

kestra:
plugins:
configurations:
- type: io.kestra.plugin.core.trigger.Schedule
values:
# available options: LAST | NONE | ALL -- default: ALL
recoverMissedSchedules: NONE

The recoverMissedSchedules configuration can be set to ALL, NONE or LAST:

  • ALL: Kestra will recover all missed schedules. This is the default value.
  • NONE: Kestra will not recover any missed schedules.
  • LAST: Kestra will recover only the last missed schedule for each flow.

Note that this is a global configuration that will apply to all flows, unless other behavior is explicitly defined within the flow definition like below:

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "*/15 * * * *"
recoverMissedSchedules: NONE

In this example, the recoverMissedSchedules is set to NONE, which means that Kestra will not recover any missed schedules for this specific flow regardless of the global configuration or default recoverMissedSchedules behavior. If you have a missed window of executions with recoverMissedSchedules: NONE, then use Backfill to replay the missed executions.

Using Backfill

Backfills are replays of missed schedule intervals between a defined start and end date.

To backfill the missed executions, use Backfill executions on the flow’s Triggers tab. Ensure the date range spans every missed schedule so the trigger can replay each execution. See the Backfill documentation for details.

Disabling the trigger

To pause the schedule while you decide what to do next, set disabled: true in the YAML or use the Enabled toggle in the UI. See Disabled for details.

Passing inputs to the Schedule trigger

Use the inputs property to set input values before execution:

In this example, the user input is set to “John Smith” by the schedule trigger:

id: myflow
namespace: company.team
inputs:
- id: user
type: STRING
defaults: Rick Astley
tasks:
- id: hello
type: io.kestra.plugin.core.log.Log
message: "Hello {{ inputs.user }}! 🚀"
triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "*/1 * * * *"
inputs:
user: John Smith

Disable a schedule trigger after a specified execution state

The stopAfter property disables the trigger when the execution reaches one of the specified states — for example, FAILED or KILLED — preventing repeated runs of a broken flow until you manually re-enable it.

id: myflow
namespace: company.team
inputs:
- id: user
type: STRING
defaults: Rick Astley
tasks:
- id: hello
type: io.kestra.plugin.core.log.Log
message: "Hello {{ inputs.user }}! 🚀"
triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "*/1 * * * *"
stopAfter:
- FAILED
- KILLED
inputs:
user: John Smith

Detect stuck Schedule Triggers

Kestra has a plugin, ScheduleMonitor, for detecting stuck or misconfigured Schedule Triggers. It checks periodically and can run at the Tenant level, for a specific Namespace, or for a single Flow.

For example, set this up as a System Flow and send an alert if any Schedule Triggers come back showing an issue:

id: detect_stuck_schedules
namespace: system
tasks:
- id: send_alert
runIf: "{{ trigger.data }}"
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
url: https://kestra.io/api/mock
messageText: The following Schedule triggers seem unhealthy {{ trigger.data }}
triggers:
- id: stuck_schedules
type: io.kestra.plugin.kestra.triggers.ScheduleMonitor
auth:
username: "{{ secret('KESTRA_USERNAME') }}"
password: "{{ secret('KESTRA_PASSWORD') }}"
namespace: company.team
flowId: daily_sync
interval: PT1H # poll for stuck schedules every 1h

By default, the trigger checks all schedules in the current Tenant (Multi-tenancy is an Enterprise feature) if no Namespace or Flow is specified.

Was this page helpful?