Argo Workflows’ cron functionality is actually a distributed scheduler, not a traditional cron daemon running on a single node.

Let’s look at how it works with a concrete example. Imagine you want to run a simple "hello world" workflow every minute.

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: hello-world-template
spec:
  entrypoint: hello
  templates:
  - name: hello
    container:
      image: alpine:latest
      command: ["echo"]
      args: ["Hello, Argo Cron!"]
---
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
  name: hello-world-cron
spec:
  schedule: "*/1 * * * *" # Every minute
  workflowSpec:
    entrypoint: hello
    templates:
    - name: hello
      container:
        image: alpine:latest
        command: ["echo"]
        args: ["Hello, Argo Cron!"]

When you apply this, Argo doesn’t start a cron process. Instead, the Argo Controller (specifically, the cronworkflow-controller) polls the Kubernetes API for CronWorkflow objects. For each CronWorkflow, it checks the schedule field. If the current time matches the schedule, it creates a new Workflow object based on the workflowSpec defined in the CronWorkflow. This new Workflow is then picked up and executed by the workflow-controller like any other workflow.

This distributed nature means Argo Cron scales with your Kubernetes cluster and doesn’t have a single point of failure like a traditional cron daemon. The workflow-controller handles the actual execution of the generated workflows.

The core problem Argo Cron solves is providing reliable, Kubernetes-native scheduling for your containerized jobs. It integrates directly into your Kubernetes workflows, allowing you to define schedules for complex multi-step processes just as easily as you would a single command.

The schedule field uses standard cron syntax. */1 * * * * means "at every minute." You can get much more granular or complex, for example:

  • 0 1 * * *: Daily at 1 AM.
  • 0 0 1 * *: On the first day of every month at midnight.
  • @hourly: A shorthand for 0 * * * *.

The concurrencyPolicy field controls how concurrent runs are handled. The default is Allow, meaning multiple instances of the workflow can run simultaneously if a new one is scheduled before the previous one finishes. Other options are:

  • Forbid: If a previous instance is still running, the new scheduled instance will be skipped.
  • Replace: If a previous instance is still running, it will be terminated and replaced by the new instance.

The startingDeadlineSeconds field is crucial for ensuring jobs don’t get missed. If a workflow is scheduled but doesn’t get created within this time window (e.g., due to controller downtime), it will be skipped. Setting this to a reasonable value like 600 (10 minutes) ensures that if the controller is down for a bit, it won’t try to catch up on every missed schedule.

Consider the successfulJobsHistoryLimit and failedJobsHistoryLimit fields. These control how many completed and failed Workflow objects are retained for a given CronWorkflow. By default, Argo keeps 10 successful and 3 failed jobs. If you have frequent runs and don’t need to keep a long history of individual workflow executions, reducing these limits can prevent the Kubernetes API server from being overloaded with objects. For example, successfulJobsHistoryLimit: 3 and failedJobsHistoryLimit: 1.

The most surprising thing about Argo Cron is how it leverages Kubernetes events and controllers to achieve scheduling. It doesn’t run a persistent scheduler process; instead, the cronworkflow-controller continuously watches CronWorkflow resources and, based on their schedules and the current time, programmatically creates Workflow resources. The workflow-controller then takes over to execute these newly created Workflow resources. This means your scheduling logic is entirely managed by Kubernetes, making it highly resilient and scalable.

If you’ve set up a CronWorkflow and it’s not running, the first place to check is the CronWorkflow object itself. Use kubectl get cronworkflow <your-cron-workflow-name> -o yaml and look for a Status field. It often contains messages indicating why a workflow isn’t being scheduled, such as Next schedule time in XmYs or error messages if the schedule is invalid.

The next thing you’ll likely run into is managing the output and artifact storage for your scheduled workflows.

Want structured learning?

Take the full Argo-workflows course →