Skip to content

Cron Workflows

v2.5 and after

CronWorkflows are workflows that run on a schedule. They are designed to wrap a workflowSpec and to mimic the options of Kubernetes CronJobs. In essence, CronWorkflow = Workflow + some specific cron options.

CronWorkflow Spec

Below is an example CronWorkflow:

apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
  name: test-cron-wf
spec:
  schedule: "* * * * *"
  concurrencyPolicy: "Replace"
  startingDeadlineSeconds: 0
  workflowSpec:
    entrypoint: date
    templates:
    - name: date
      container:
        image: alpine:3.6
        command: [sh, -c]
        args: ["date; sleep 90"]

workflowSpec and workflowMetadata

CronWorkflow.spec.workflowSpec is the same type as Workflow.spec. It is a template for Workflow objects created from it.

The Workflow name is generated based on the CronWorkflow name. In the above example it would be similar to test-cron-wf-tj6fe.

You can use CronWorkflow.spec.workflowMetadata to add labels and annotations.

CronWorkflow Options

Option Name Default Value Description
schedule None Cron schedule to run Workflows. Example: 5 4 * * *. Deprecated, use schedules.
schedules None v3.6 and after: List of Cron schedules to run Workflows. Example: 5 4 * * *, 0 1 * * *. Either schedule or schedules must be provided.
timezone Machine timezone IANA Timezone to run Workflows. Example: America/Los_Angeles
suspend false If true Workflow scheduling will not occur. Can be set from the CLI, GitOps, or directly
concurrencyPolicy Allow What to do if multiple Workflows are scheduled at the same time. Allow: allow all, Replace: remove all old before scheduling new, Forbid: do not allow any new while there are old
startingDeadlineSeconds 0 Seconds after the last scheduled time during which a missed Workflow will still be run.
successfulJobsHistoryLimit 3 Number of successful Workflows to persist
failedJobsHistoryLimit 1 Number of failed Workflows to persist
stopStrategy.expression nil v3.6 and after: defines if the CronWorkflow should stop scheduling based on an expression, which if present must evaluate to false for the workflow to be created
when None v3.6 and after: An optional expression which will be evaluated on each cron schedule hit and the workflow will only run if it evaluates to true

Cron Schedule Syntax

The cron scheduler uses standard cron syntax. The implementation is the same as CronJobs, using robfig/cron.

Crash Recovery

If the Controller crashes, you can ensure that any missed schedules still run.

With startingDeadlineSeconds you can specify a maximum grace period past the last scheduled time during which it will still run. For example, if a CronWorkflow that runs every minute is last run at 12:05:00, and the controller crashes between 12:05:55 and 12:06:05, then the expected execution time of 12:06:00 would be missed. However, if startingDeadlineSeconds is set to a value greater than 5 (the time passed between the last scheduled time of 12:06:00 and the current time of 12:06:05), then a single instance of the CronWorkflow will be executed exactly at 12:06:05.

Currently only a single instance will be executed as a result of setting startingDeadlineSeconds.

This setting can also be configured in tandem with concurrencyPolicy to achieve more fine-tuned control.

Daylight Saving

When using timezone, Daylight Saving Time (DST) is taken into account. Depending on the local time of the scheduled workflow, it will run once, twice, or not at all when the clock moves forward or back.

For example, with timezone: America/Los_Angeles:

  • +1 hour (DST start) at 2020-03-08 02:00:00:

    Note: The schedules between 02:00 a.m. to 02:59 a.m. were skipped on Mar 8th due to the clock being moved forward:

    cron sequence workflow execution time
    59 1 ** * 1 2020-03-08 01:59:00 -0800 PST
    2 2020-03-09 01:59:00 -0700 PDT
    3 2020-03-10 01:59:00 -0700 PDT
    0 2 ** * 1 2020-03-09 02:00:00 -0700 PDT
    2 2020-03-10 02:00:00 -0700 PDT
    3 2020-03-11 02:00:00 -0700 PDT
    1 2 ** * 1 2020-03-09 02:01:00 -0700 PDT
    2 2020-03-10 02:01:00 -0700 PDT
    3 2020-03-11 02:01:00 -0700 PDT
  • -1 hour (DST end) at 2020-11-01 02:00:00:

    Note: the schedules between 01:00 a.m. to 01:59 a.m. were triggered twice on Nov 1st due to the clock being set back:

    cron sequence workflow execution time
    59 1 ** * 1 2020-11-01 01:59:00 -0700 PDT
    2 2020-11-01 01:59:00 -0800 PST
    3 2020-11-02 01:59:00 -0800 PST
    0 2 ** * 1 2020-11-01 02:00:00 -0800 PST
    2 2020-11-02 02:00:00 -0800 PST
    3 2020-11-03 02:00:00 -0800 PST
    1 2 ** * 1 2020-11-01 02:01:00 -0800 PST
    2 2020-11-02 02:01:00 -0800 PST
    3 2020-11-03 02:01:00 -0800 PST

Skip forward (missing schedule)

You can use when to schedule once per day, even if the time you want is in a daylight saving skip forward period where it would otherwise be scheduled twice.

An example 02:30:00 schedule

schedules:
  - 30 2 * * *
  - 0 3 * * *
when: "{{= cronworkflow.lastScheduledTime == nil || (now() - cronworkflow.lastScheduledTime).Seconds() > 3600 }}"

The 3:00 run of the schedule will not be scheduled every day of the year except on the day when the clock leaps forward over 2:30. The when expression prevents this workflow from running more than once an hour. In that case the 3:00 run will run, as 2:30 was skipped over.

Can run at 3:00

If you create this CronWorkflow between the desired time and 3:00 it will run at 3:00 as it has never run before.

Skip backwards (duplication)

You can use when to schedule once per day, even if the time you want is in a daylight saving skip backwards period where it would otherwise not be scheduled.

An example 01:30:00 schedule

schedules:
  - 30 1 * * *
when: "{{= cronworkflow.lastScheduledTime == nil || (now() - cronworkflow.lastScheduledTime).Seconds() > 7200 }}"

This will schedule at the first 01:30 on a skip backwards change. The second will not run because of the when expression, which prevents this workflow running more often than once every 2 hours..

Automatically Stopping a CronWorkflow

v3.6 and after

You can configure a CronWorkflow to automatically stop based on an expression with stopStrategy.expression. You can use the variables cronworkflow.failed and cronworkflow.succeeded.

For example, if you want to stop scheduling new workflows after one success:

stopStrategy:
  expression: "cronworkflow.succeeded >= 1"

You can also stop scheduling new workflows after three failures with:

stopStrategy:
  expression: "cronworkflow.failed >= 3"

Scheduling vs. Completions

Depending on the time it takes to schedule and run a workflow, the number of completions can exceed the configured maximum.

For example, if you configure the CronWorkflow to schedule every minute (* * * * *) and stop after one success (cronworkflow.succeeded >= 1). If the Workflow takes 90 seconds to run, the CronWorkflow will actually stop after two completions. This is because when the stopping condition is achieved, there is already another Workflow running. For that reason, prefer conditions like cronworkflow.succeeded >= 1 over cronworkflow.succeeded == 1.

Managing CronWorkflow

CLI

You can create CronWorkflows with the CLI:

$ argo cron create cron.yaml
Name:                          test-cron-wf
Namespace:                     argo
Created:                       Mon Nov 18 10:17:06 -0800 (now)
Schedules:                     * * * * *
Suspended:                     false
StartingDeadlineSeconds:       0
ConcurrencyPolicy:             Forbid

$ argo cron list
NAME           AGE   LAST RUN   SCHEDULES    SUSPENDED
test-cron-wf   49s   N/A        * * * * *   false

# some time passes

$ argo cron list
NAME           AGE   LAST RUN   SCHEDULES    SUSPENDED
test-cron-wf   56s   2s         * * * * *   false

$ argo cron get test-cron-wf
Name:                          test-cron-wf
Namespace:                     argo
Created:                       Wed Oct 28 07:19:02 -0600 (23 hours ago)
Schedules:                      * * * * *
Suspended:                     false
StartingDeadlineSeconds:       0
ConcurrencyPolicy:             Replace
LastScheduledTime:             Thu Oct 29 06:51:00 -0600 (11 minutes ago)
NextScheduledTime:             Thu Oct 29 13:03:00 +0000 (32 seconds from now)
Active Workflows:              test-cron-wf-rt4nf

Note: NextScheduledRun assumes the Controller uses UTC as its timezone

kubectl

You can use kubectl apply -f and kubectl get cwf

Back-Filling Days

See cron backfill.

GitOps via Argo CD

You can manage CronWorkflow resources with GitOps by using Argo CD

UI

You can also manage CronWorkflow resources in the UI


Have a question?

Search on GitHub Discussions and Slack.