Anthony Goddard
09/23/2025, 1:15 AMMarvin
09/23/2025, 1:15 AMMarvin
09/23/2025, 1:20 AMPREFECT_API_*
prefix instead of PREFECT_SERVER_*
.
- Loop cadence and batching
- PREFECT_SERVER_SERVICES_SCHEDULER_LOOP_SECONDS
(default 60)
- PREFECT_SERVER_SERVICES_SCHEDULER_DEPLOYMENT_BATCH_SIZE
(default 100)
- PREFECT_SERVER_SERVICES_SCHEDULER_INSERT_BATCH_SIZE
(default 500)
- Horizon and run count guardrails
- PREFECT_SERVER_SERVICES_SCHEDULER_MIN_SCHEDULED_TIME
(default 1h)
- PREFECT_SERVER_SERVICES_SCHEDULER_MAX_SCHEDULED_TIME
(default 100d)
- PREFECT_SERVER_SERVICES_SCHEDULER_MIN_RUNS
(default 3)
- PREFECT_SERVER_SERVICES_SCHEDULER_MAX_RUNS
(default 100)
- Recent deployments loop
- PREFECT_SERVER_SERVICES_SCHEDULER_RECENT_DEPLOYMENTS_LOOP_SECONDS
(default 5)
Docs:
- Schedules (concepts)
- Settings reference (see Server services -> Scheduler)
Recommended changes for your case
- Reduce the scheduler horizon and run counts:
- Set a smaller horizon for high-frequency schedules (e.g., 5–10 minutes).
- Lower min runs if your schedules are frequent (e.g., 1).
- Cap max runs to a small number (e.g., 10–20).
- Disable catchup on these schedules so the scheduler doesn’t try to create a backlog of past runs when it falls behind or on redeploy.
- If you have many deployments, consider consolidating schedules into fewer deployments (preferably one deployment with many schedules) to reduce per-loop work.
Example (Server) — put these in your profile or environment
PREFECT_SERVER_SERVICES_SCHEDULER_MIN_SCHEDULED_TIME=PT10M
PREFECT_SERVER_SERVICES_SCHEDULER_MIN_RUNS=1
PREFECT_SERVER_SERVICES_SCHEDULER_MAX_RUNS=10
# Optional: if you have many deployments and want the scheduler to keep up
PREFECT_SERVER_SERVICES_SCHEDULER_DEPLOYMENT_BATCH_SIZE=200
# Optional: if you’re inserting many runs in bursts and your DB can handle it
PREFECT_SERVER_SERVICES_SCHEDULER_INSERT_BATCH_SIZE=1000
For Prefect Cloud, use the same variables with PREFECT_API_SERVICES_SCHEDULER_*
instead.
Worker choice (process vs Docker)
- Process worker
- Pros: Minimal startup overhead, best for lots of short/fast runs; higher perceived throughput on the same machine.
- Cons: Less isolation; you must manage Python/env deps on the machine.
- Docker worker
- Pros: Strong isolation, reproducible runtime, easy dependency management.
- Cons: Extra overhead to start containers/pull images; can be noticeable at 2–5 minute intervals with short flows.
Other considerationsMarvin
09/23/2025, 1:20 AMcatchup
enabled?
If you share those details, I can suggest specific values for the scheduler settings and any DB tuning that’ll get you back to quick operations.Anthony Goddard
09/23/2025, 1:29 AMMarvin
09/23/2025, 1:29 AMMarvin
09/23/2025, 1:38 AMprefect deployment schedule ls
- prefect deployment schedule pause <deployment_name> <schedule_id>
- Optionally set a temporary work pool concurrency limit to 0 to stop execution while you clean up:
- prefect work-pool set-concurrency-limit <pool_name> 0
2) Reduce the scheduler’s horizon and run counts (Server env)
Set these and restart the API:
PREFECT_SERVER_SERVICES_SCHEDULER_MIN_SCHEDULED_TIME=PT5M
PREFECT_SERVER_SERVICES_SCHEDULER_MIN_RUNS=1
PREFECT_SERVER_SERVICES_SCHEDULER_MAX_RUNS=10
# Optional: if you have lots of deployments/schedules
PREFECT_SERVER_SERVICES_SCHEDULER_DEPLOYMENT_BATCH_SIZE=200
# Optional: if inserts are efficient on your DB
PREFECT_SERVER_SERVICES_SCHEDULER_INSERT_BATCH_SIZE=1000
Docs: Schedules, Settings reference
3) Recreate schedules “from now” so they don’t backfill
- Clear schedules on the deployment:
- prefect deployment schedule clear <deployment_name>
- Recreate schedules with an anchor date at or after now (prevents backfilling older intervals):
- prefect deployment schedule create <deployment_name> --interval 600 --anchor-date "2025-09-23T12:00:00Z"
- Repeat per schedule with your desired interval and a recent anchor-date.
- Resume schedules or restore your work pool concurrency.
4) Switch to process workers for short jobs
- Process workers have minimal startup overhead and will help 2–10s flows hit your target runtimes.
- Docker workers add container/image overhead that’s noticeable at 2–5 minute intervals and short runs.
5) Database pool sizing (Postgres)
Prefect’s API defaults are conservative. Increase the SQLAlchemy connection pool for the server:
PREFECT_SERVER_DATABASE_POOL_SIZE=20
PREFECT_SERVER_DATABASE_MAX_OVERFLOW=40
PREFECT_SERVER_DATABASE_POOL_TIMEOUT=30
PREFECT_SERVER_DATABASE_POOL_RECYCLE=1800
After setting, restart the API. If your Postgres is under-provisioned for concurrent connections, increase its max_connections and ensure autovacuum is keeping up.
6) Reduce block/API chatter in short runs
- Load blocks once per run and reuse the object (avoid repeated Block.load(...)
calls inside many short tasks).
- For hottest secrets/creds, consider passing them via environment variables in your work pool’s base job template instead of pulling blocks on every run.
- If possible, use provider-native auth (e.g., IAM/Workload Identity) to avoid any block fetch.
Why “one deployment with many schedules” can still be heavy
- It is generally better than many deployments, but the main cost is total future runs created. Tightening the scheduler horizon and min/max run counts is what actually reduces load.
Optional: Late runs service
If you see excessive chatter from “late runs” handling, you can tune it:
PREFECT_SERVER_SERVICES_LATE_RUNS_LOOP_SECONDS=5
PREFECT_SERVER_SERVICES_LATE_RUNS_AFTER_SECONDS=PT30S
See settings: Settings reference
A couple of quick questions so I can give you concrete values:Marvin
09/23/2025, 1:38 AMAnthony Goddard
09/23/2025, 6:53 AMMarvin
09/23/2025, 6:53 AMMarvin
09/23/2025, 6:54 AMPREFECT_SERVER_SERVICES_SCHEDULER_MIN_SCHEDULED_TIME=PT2M
- PREFECT_SERVER_SERVICES_SCHEDULER_MIN_RUNS=1
- PREFECT_SERVER_SERVICES_SCHEDULER_MAX_RUNS=5
- Make the scheduler loop more responsive:
- PREFECT_SERVER_SERVICES_SCHEDULER_LOOP_SECONDS=10
- Keep batch sizes tuned up if your DB can handle it:
- PREFECT_SERVER_SERVICES_SCHEDULER_DEPLOYMENT_BATCH_SIZE=200
- PREFECT_SERVER_SERVICES_SCHEDULER_INSERT_BATCH_SIZE=1000
- If you see a lot of “late runs” churn, dampen late-run handling noise:
- PREFECT_SERVER_SERVICES_LATE_RUNS_AFTER_SECONDS=PT60S
Docs: Schedules, Settings reference
Make sure schedules don’t backfill
- Clear and recreate schedules with a recent --anchor-date
so only future runs get created:
- prefect deployment schedule clear <deployment_name>
- prefect deployment schedule create <deployment_name> --interval 120 --anchor-date "<recent-UTC-ISO8601>"
Increase execution throughput (reduce late runs)
- Use process workers for these short runs (2–10s) — they’re much snappier than Docker workers.
- Scale out workers so total parallel capacity exceeds your per-minute arrival rate of runs. If you’re seeing late runs when “upcoming” hits ~140, your workers are probably not keeping up burst-wise.
- Reduce per-run overhead:
- Load blocks once per run and reuse; avoid repeated Block.load(...)
calls in tasks.
- For hottest secrets/creds, consider env vars via your work pool’s base job template instead of block fetches on every run.
- Reduce app logs for these very short runs (less API chatter).
API/UI and DB tuning
- Postgres pool (server-side):
- PREFECT_SERVER_DATABASE_POOL_SIZE=20
- PREFECT_SERVER_DATABASE_MAX_OVERFLOW=40
- PREFECT_SERVER_DATABASE_POOL_TIMEOUT=30
- PREFECT_SERVER_DATABASE_POOL_RECYCLE=1800
- Ensure Postgres has enough CPU/IO, autovacuum is keeping up, and table/index bloat is under control, especially on flow_runs
. If the table has grown large from earlier backfill, consider purging old SCHEDULED/late runs you don’t need.
- Optionally reduce the default API list size (smaller responses can help UI snappiness):
- PREFECT_SERVER_API_DEFAULT_LIMIT=100
(default is 200)
Quick cleanup of old scheduled runs (optional)
If you want to quickly shrink “noise” and DB pressure while tuning:
- Pause schedules:
- prefect deployment schedule ls
- prefect deployment schedule pause <deployment_name> <schedule_id>
- Delete stale SCHEDULED runs older than a window (e.g., >30 minutes ago) with a small Python script using the Prefect client. I can share a snippet if you want.
Structural option (biggest impact if feasible)
- If many of those 80 schedules are just parameter variants of the same job, consider:
- A single high-frequency “launcher” schedule that computes the 80 variants and runs them in-process (tasks) or spawns subflows programmatically — this reduces the number of scheduler-managed runs dramatically. Only do this if you don’t need totally independent history/concurrency per variant.
A couple of checks/questions to fine-tune next steps
- Are you running process workers now? How many concurrent flow runs can your workers handle at peak?
- Rough size of the flow_runs
table and indexes? Any recent growth from earlier backfill?
- How chatty are runs (log volume)? If high, we can adjust logging:
- PREFECT_LOGGING_TO_API_BATCH_INTERVAL=5
- Reduce log level for these flows.Marvin
09/23/2025, 6:54 AMNate
09/23/2025, 2:40 PMAnthony Goddard
09/23/2025, 7:44 PMNate
09/23/2025, 9:07 PMI'm a new user and I've been digging into everything I can w/ Prefect recentlywelcome! i mention redis for messaging as its one of the things you'd eventually have to do to scale a prefect server installation! let me know if you develop any questions on that!
Anthony Goddard
09/23/2025, 9:19 PMPREFECT_SERVER_API_DEFAULT_LIMIT
)Anthony Goddard
09/25/2025, 5:23 AMAnthony Goddard
09/25/2025, 5:24 AMPREFECT_SERVER_SERVICES_SCHEDULER_MIN_SCHEDULED_TIME=PT2M
- PREFECT_SERVER_SERVICES_SCHEDULER_MIN_RUNS=1
?Marvin
09/25/2025, 5:24 AMMarvin
09/25/2025, 5:26 AM