Skip to main content

What are Schedules?

Schedules allow you to automatically trigger workflow executions at specified times or intervals. Instead of manually starting workflows, you can configure them to run:
  • On a cron schedule: Run at specific times (e.g., every day at 9 AM)
  • At regular intervals: Run repeatedly with a fixed delay (e.g., every 5 minutes)
  • On calendar dates: Run on specific days of the month or week

Key Features

Multiple Schedule Types

Choose from cron expressions, intervals, or calendar-based scheduling.

Overlap Policies

Control what happens when a new run is triggered while a previous run is still executing.

Dynamic Management

Create, pause, resume, and delete schedules at runtime without redeploying.

Backfill Support

Catch up on missed runs after downtime with backfill capabilities.

Creating Schedules

There are two ways to create schedules:

Using the Decorator

The @scheduled_workflow decorator combines workflow definition with schedule configuration:

Using the API

For dynamic schedule creation, use the create_schedule function:

Schedule Types

Cron Expressions

Cron expressions provide precise control over when workflows run. The format is:
Common patterns:

Intervals

Intervals specify a fixed duration between runs:
Supported units:
  • s - seconds
  • m - minutes
  • h - hours
  • d - days
When using intervals, the first run happens immediately when the schedule is created. Subsequent runs occur at the specified interval after each run completes.

Calendar-Based Schedules

For more complex scheduling needs, use calendar specifications:
CalendarSpec fields:

Overlap Policies

When a schedule triggers while a previous run is still executing, the overlap policy determines what happens:
BUFFER_ALL can lead to unbounded queue growth if runs take longer than the schedule interval. Use SKIP or BUFFER_ONE for most use cases.

Choosing an Overlap Policy

Use when it’s safe to miss a run. The next scheduled run will catch up.Example: Metrics collection, status checks
Use when you need to guarantee at least one run happens after a long-running execution.Example: Data synchronization
Use when only the most recent run matters and older runs should be cancelled.Example: Cache refresh
Use when runs are independent and can execute concurrently.Example: Processing independent queues

Managing Schedules

Pause and Resume

Temporarily stop a schedule without deleting it:

Update Schedule

Modify an existing schedule’s configuration:

Delete Schedule

Remove a schedule (soft delete - record is preserved for audit):

List Schedules

Query existing schedules:

Manual Trigger and Backfill

Manual Trigger

Execute a scheduled workflow immediately, outside of its normal schedule:
This is useful for:
  • Testing the workflow
  • Running on-demand when needed
  • Recovering from issues

Backfill Missed Runs

If the scheduler was down and missed some runs, you can backfill them:
Backfill creates runs for all scheduled times in the range. For high-frequency schedules, this could create many runs. Consider the overlap_policy when backfilling.

Timezone Support

Schedules support timezone-aware execution:
All schedule times are stored internally as UTC. The timezone is used to calculate the correct UTC time for each run.

Time Bounds

Limit when a schedule is active:

Running the Scheduler

PyWorkflow supports two runtimes for schedule execution:
For development, testing, or single-process deployments, use the local scheduler:
The local scheduler polls storage for due schedules and triggers workflows in-process.
Both schedulers:
  1. Poll storage for due schedules (every 5 seconds by default)
  2. Trigger workflow execution for due schedules
  3. Update next_run_time after each run
  4. Handle overlap policies automatically
The schedule primitives (trigger_schedule, backfill_schedule, etc.) are runtime-agnostic and will use whichever runtime is configured. You can switch between local and Celery without changing your schedule management code.

Activating Decorator-Based Schedules

When using @scheduled_workflow, the schedules need to be activated to create records in storage:
Call this during application startup to ensure all decorated workflows have corresponding schedule records.

Complete Examples

Every-Minute Schedule with Celery (Distributed)

This example shows a workflow that runs every minute using Celery workers for distributed execution. 1. Define the scheduled workflow (myapp/workflows.py):
2. Create a startup script (myapp/main.py):
3. Start the services:
The scheduler will now trigger minute_health_check every minute, and the Celery worker will execute it.

Every-Minute Schedule with Local Runtime

For testing or simple use cases, you can run schedules locally without Celery. 1. Define the workflow (local_schedule.py):
2. Run it:
Output:
The local runtime is great for development and testing. For production, use the Celery-based approach with pyworkflow worker run --beat for robust, distributed schedule execution.

CLI Commands

Manage schedules from the command line:

Best Practices

The SKIP overlap policy is the safest default. It prevents resource exhaustion and ensures predictable behavior.
Don’t schedule too frequently. Consider the typical workflow duration when setting intervals to avoid constant overlaps.
Scheduled workflows should handle transient failures with retries. Use step-level retries for resilience.
Track successful_runs, failed_runs, and skipped_runs to identify issues with your schedules.
Design scheduled workflows to be idempotent. They may run multiple times due to retries or backfills.
Be explicit about timezones, especially for schedules that should run at specific local times.

Schedule Status

Schedules can be in one of three states:

Monitoring

Track schedule execution with built-in statistics:

Next Steps

CLI Reference

Learn the full CLI commands for schedule management.

Workflows

Understand workflow concepts and patterns.

Fault Tolerance

Configure auto recovery for scheduled workflows.

Events

Track schedule events in the event log.