Prefect IntervalSchedule Migration: From Legacy Schedules to Prefect 3

·5 min read·Automation

Migrate from deprecated prefect.server.schemas.schedules IntervalSchedule to Prefect 3 scheduling patterns — with side-by-side code comparisons, common errors, and a step-by-step upgrade path.

Prefect IntervalSchedule Migration: From Legacy Schedules to Prefect 3AI Generated Image

If you have landed here from an import error, you are not alone. from prefect.server.schemas.schedules import IntervalSchedule stopped working and nobody told you.

Prefect 3 changed how schedules work. The old prefect.server.schemas.schedules module is gone. The scheduling model moved from server-side schema objects to deployment-level configuration. The concepts are the same but the code is different.

This guide covers the complete migration path: what changed, why, and exactly how to update your code with working examples.

# Who This Is For

  • Data engineers with Prefect 2 pipelines that broke after upgrading to Prefect 3
  • Developers getting ImportError: cannot import name 'IntervalSchedule' and needing a fix
  • Vibe coders setting up new Prefect workflows and finding outdated tutorials referencing old imports
  • Teams planning a Prefect 2 to 3 migration who need to understand what changed

Familiarity with Prefect basics (flows and tasks) is assumed. If you are new to Prefect, see Schedule and Orchestrate Workflows with Prefect first.

# What Changed

flowchart LR
  subgraph Prefect2["Prefect 2 (Legacy)"]
    IS["IntervalSchedule\n(server schema)"]
    CS["CronSchedule\n(server schema)"]
    RS["RRuleSchedule\n(server schema)"]
  end
  subgraph Prefect3["Prefect 3 (Current)"]
    D["Deployment\nschedules=[\n  {...}\n]"]
  end
  IS -.->|"migrated to"| D
  CS -.->|"migrated to"| D
  RS -.->|"migrated to"| D

# The Core Change

Aspect Prefect 2 Prefect 3
Import from prefect.server.schemas.schedules import IntervalSchedule No separate import needed
Schedule object IntervalSchedule(interval=timedelta(hours=1)) {"interval": 3600} (dict)
Attachment Deployment.build_from_flow(..., schedule=schedule) flow.serve(schedules=[...]) or flow.deploy(schedules=[...])
Schedule types IntervalSchedule, CronSchedule, RRuleSchedule All expressed as dicts with interval, cron, or rrule key
Server dependency Schedule enforced by Prefect server Schedule managed by deployment

# The Common Error

When you upgrade Prefect and run existing code, you get:

text
ImportError: cannot import name 'IntervalSchedule' from 'prefect.server.schemas.schedules'

Or sometimes:

text
ModuleNotFoundError: No module named 'prefect.server.schemas.schedules'

This is not a bug. The module was intentionally removed. Here is how to fix it.

# Step 1: Replace IntervalSchedule Imports

# Before (Prefect 2)

python
from datetime import timedelta
from prefect import flow
from prefect.server.schemas.schedules import IntervalSchedule

@flow
def daily_pipeline():
    """Run the daily data pipeline."""
    print("Pipeline running")

# Create schedule
schedule = IntervalSchedule(interval=timedelta(hours=24))

# Deploy with schedule
daily_pipeline.serve(name="daily-pipeline", schedule=schedule)

# After (Prefect 3)

python
from prefect import flow

@flow
def daily_pipeline():
    """Run the daily data pipeline."""
    print("Pipeline running")

# Deploy with inline schedule
daily_pipeline.serve(
    name="daily-pipeline",
    schedules=[
        {"interval": 86400}  # 24 hours in seconds
    ],
)

No import needed. The schedule is a plain dictionary.

# Step 2: Migrate CronSchedule

Cron schedules follow the same pattern.

# Before (Prefect 2)

python
from prefect.server.schemas.schedules import CronSchedule

schedule = CronSchedule(cron="0 8 * * *", timezone="Europe/London")
my_flow.serve(name="morning-job", schedule=schedule)

# After (Prefect 3)

python
my_flow.serve(
    name="morning-job",
    schedules=[
        {"cron": "0 8 * * *", "timezone": "Europe/London"}
    ],
)

# Cron Quick Reference

Pattern Meaning
0 8 * * * Daily at 8am
0 */6 * * * Every 6 hours
0 8 * * 1-5 Weekdays at 8am
0 0 1 * * First day of each month
*/15 * * * * Every 15 minutes

# Step 3: Migrate RRuleSchedule

For complex recurrence rules, RRule schedules become dict-based too.

# Before (Prefect 2)

python
from prefect.server.schemas.schedules import RRuleSchedule

schedule = RRuleSchedule(
    rrule="FREQ=WEEKLY;BYDAY=MO,WE,FR;BYHOUR=9;BYMINUTE=0",
    timezone="Europe/London",
)
my_flow.serve(name="mwf-job", schedule=schedule)

# After (Prefect 3)

python
my_flow.serve(
    name="mwf-job",
    schedules=[
        {
            "rrule": "FREQ=WEEKLY;BYDAY=MO,WE,FR;BYHOUR=9;BYMINUTE=0",
            "timezone": "Europe/London",
        }
    ],
)

# Step 4: Multiple Schedules on One Deployment

Prefect 3 supports multiple schedules per deployment — something that was awkward in Prefect 2.

python
@flow
def reporting_pipeline():
    """Generate reports on multiple schedules."""
    print("Report generated")

reporting_pipeline.serve(
    name="multi-schedule-reports",
    schedules=[
        {"cron": "0 8 * * 1-5", "timezone": "Europe/London"},   # Weekday morning
        {"cron": "0 18 * * 5", "timezone": "Europe/London"},     # Friday evening summary
        {"cron": "0 9 1 * *", "timezone": "Europe/London"},      # Monthly on the 1st
    ],
)

# Step 5: Migrate Deployment Files

If you used deployment.yaml or prefect.yaml, the schedule syntax also changed.

# Before (prefect.yaml — Prefect 2)

yaml
deployments:
  - name: daily-pipeline
    flow_name: daily_pipeline
    schedule:
      interval: 86400

# After (prefect.yaml — Prefect 3)

yaml
deployments:
  - name: daily-pipeline
    entrypoint: pipeline.py:daily_pipeline
    schedules:
      - interval: 86400
      # Or cron:
      # - cron: "0 8 * * *"
      #   timezone: "Europe/London"

Note the key changes: schedule (singular) became schedules (plural, list), and flow_name became entrypoint.

# Step 6: Automated Migration Script

If you have many files to update, this script finds and flags all legacy imports.

python
import os
import re

def find_legacy_schedules(directory: str) -> list[dict]:
    """Scan Python files for deprecated Prefect schedule imports."""
    legacy_patterns = [
        r"from\s+prefect\.server\.schemas\.schedules\s+import",
        r"IntervalSchedule\(",
        r"CronSchedule\(",
        r"RRuleSchedule\(",
    ]

    findings = []
    for root, dirs, files in os.walk(directory):
        dirs[:] = [d for d in dirs if d not in {"node_modules", ".venv", "__pycache__"}]
        for filename in files:
            if not filename.endswith(".py"):
                continue
            filepath = os.path.join(root, filename)
            with open(filepath) as f:
                lines = f.readlines()
            for i, line in enumerate(lines, 1):
                for pattern in legacy_patterns:
                    if re.search(pattern, line):
                        findings.append({
                            "file": filepath,
                            "line": i,
                            "content": line.strip(),
                            "pattern": pattern,
                        })

    print(f"Found {len(findings)} legacy schedule references")
    for f in findings:
        print(f"  {f['file']}:{f['line']}{f['content']}")
    return findings

# Usage
find_legacy_schedules("./src")

# Migration Checklist

text
Prefect Schedule Migration Checklist
─────────────────────────────────────
✓ Remove all imports from prefect.server.schemas.schedules
✓ Replace IntervalSchedule(interval=timedelta(...)) with {"interval": seconds}
✓ Replace CronSchedule(cron="...") with {"cron": "..."}
✓ Replace RRuleSchedule(rrule="...") with {"rrule": "..."}
✓ Change schedule= to schedules=[] (singular to plural list)
✓ Update prefect.yaml: schedule → schedules (list)
✓ Update flow_name → entrypoint in YAML
✓ Test each deployment: prefect deployment run <name>
✓ Verify schedules in Prefect UI dashboard

# What This Replaces

Prefect 2 Pattern Prefect 3 Equivalent
from prefect.server.schemas.schedules import IntervalSchedule No import needed
IntervalSchedule(interval=timedelta(hours=1)) {"interval": 3600}
CronSchedule(cron="0 8 * * *") {"cron": "0 8 * * *"}
schedule=schedule_object schedules=[{...}]
One schedule per deployment Multiple schedules per deployment
Server-side schema validation Deployment-level configuration

# Next Steps

For a complete introduction to Prefect workflow orchestration, see Schedule and Orchestrate Workflows with Prefect. For building reusable task patterns in Prefect, see Prefect Reusable Task Blocks for Workflows.

For managing the secrets used in your Prefect deployments, see Secure Python Automation: Managing Secrets and Keys. For CI/CD integration with your Prefect pipelines, see CI/CD Pipeline for Data Workflows.

Automation services include Prefect migration, workflow design, and pipeline orchestration.

Get in touch to discuss migrating your Prefect workflows.

Enjoyed this article?

Get notified when I publish new articles on automation, ecommerce, and data engineering.

prefect.server.schemas.schedules intervalscheduleprefect intervalschedule migrationprefect.server.schemas.schedules import intervalscheduleprefect schedule migrationprefect 3 schedulingprefect intervalschedule deprecatedprefect cron scheduleprefect flow scheduleprefect deployment scheduleprefect scheduling guide