Prefect IntervalSchedule Migration: From Legacy Schedules to Prefect 3
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.
AI Generated ImageIf 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:
ImportError: cannot import name 'IntervalSchedule' from 'prefect.server.schemas.schedules'
Or sometimes:
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)
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)
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)
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)
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)
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)
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.
@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)
deployments:
- name: daily-pipeline
flow_name: daily_pipeline
schedule:
interval: 86400
# After (prefect.yaml — Prefect 3)
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.
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
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.