<@ULVA73B9P> I have two deployments which run dail...
# ask-marvin
p
@Marvin I have two deployments which run daily in the early hours. I set the cron time for 1 for 00:30 and one for 4am. It even states the correct time (i.e. 04:00 everyday) next to each deployment in the UI, and the deployments have been successfully triggering at these times for over 2 weeks! _pre_dbt_syncs_._serve_( name="pre_dbt_syncs", cron="30 0 * * *" ) _post_dbt_jobs_._serve_( name="post_dbt_jobs", cron="0 4 * * *" ) Setup: docker container running the server, then a docker container for each deployment The problem: pre_dbt_syncs last ran on 07/03/2026 at 00:30 while the post_dbt_jobs ran last at 04:00 on the 06/03/2026. In the UI, under the deployment, I do not see the next run scheduled at all What I have checked: Container logs for each deployment -> no errors and have been running for 4 days before incident Server container logs - again no errors and been running for 4 days before the incident In the server UI - Past run logs for these offending deployments - no errors In the server UI - Other deployments schedules ran and I can see the next scheduled run for them Prefect service stack restarts - 4 days before the incident Host issues - non reported, other services have no errors and no CPU or RAM spikes Edits to the deployment/server files - 2 weeks ago (and deployments have been running this long up until this point) Where should I look to troubleshoot? what is the most likely cause? I'm running prefect 3.6.19
m
thought for 595.1 seconds
Thanks for all the detail — that helps a lot. Most likely cause Given you’re using Flow.serve() in separate containers, the most common cause that matches your symptoms is: the serve runner paused the deployment schedules on a prior shutdown (default behavior), so the server stopped generating new runs. The already-generated “future” runs kept executing for a few days, then dried up — which is why you now see no “next run” in the UI and the last executions were 06–07 Mar. Why this happens - In Prefect 3.x, Flow.serve creates an API-managed deployment and server-side schedules. - By default, serve pauses its deployment schedules on shutdown (
pause_on_shutdown=True
). On startup, it does not auto-resume. - When a deployment is paused, the server’s scheduler stops creating new SCHEDULED runs. Any SCHEDULED runs that were already created in the DB will still execute; once those are consumed, nothing else is created and the UI stops showing “Next run”. How to confirm 1) List schedules and their status for each deployment:
Copy code
prefect deployment schedule ls pre_dbt_syncs/pre_dbt_syncs
prefect deployment schedule ls post_dbt_jobs/post_dbt_jobs
If they’re paused, you’ll see that indicated there. If you’re unsure of the exact names, list all deployments first:
Copy code
prefect deployment ls
2) Inspect the deployment to see schedule info:
Copy code
prefect deployment inspect pre_dbt_syncs/pre_dbt_syncs
prefect deployment inspect post_dbt_jobs/post_dbt_jobs
How to fix now - Resume the schedules:
Copy code
prefect deployment schedule resume pre_dbt_syncs/pre_dbt_syncs --all
prefect deployment schedule resume post_dbt_jobs/post_dbt_jobs --all
How to prevent this next time - Don’t auto-pause on container shutdowns/restarts:
Copy code
pre_dbt_syncs.serve(
    name="pre_dbt_syncs",
    cron="30 0 * * *",
    pause_on_shutdown=False
)

post_dbt_jobs.serve(
    name="post_dbt_jobs",
    cron="0 4 * * *",
    pause_on_shutdown=False
)
- Alternatively, explicitly resume on container start (e.g., a small bootstrap step that calls
resume_deployment
for these two deployments), or wire a one-time
prefect deployment schedule resume ... --all
into your container’s startup script. If they’re not paused Since you mentioned other deployments still show next runs, the server scheduler is almost certainly healthy. If these two are not paused: - Verify the schedules still exist and are active via
prefect deployment schedule ls ...
- Check container clocks/timezones just in case (
date -u
inside the server and runner containers) - Confirm only one runner instance is serving each deployment (duplicate runners won’t stop scheduling but can make logs confusing) - Share the output of: -
prefect deployment schedule ls <name>
-
prefect deployment inspect <name>
- A snippet of runner logs around startup/shutdown (look for “Pausing all deployments…”) But in practice, 9 times out of 10 this exact pattern is caused by the default
pause_on_shutdown=True
combined with a prior container stop/restart. Resuming the schedules should restore “Next run” in the UI and future triggers.
p
@Marvin here is the output of the inspect and schedule commands prefect deployment inspect 'pre-dbt-syncs/pre_dbt_syncs' { 'id': 'cba46cab-45d7-444a-a9cd-56fcf1ad148f', 'created': '2026-02-20T170540.543844Z', 'updated': '2026-03-02T155103.613000Z', 'name': 'pre_dbt_syncs', 'version': 'd34bb2278b2f55fbce75e72209afbe26', 'version_id': None, 'version_info': None, 'branch': None, 'base': None, 'root': None, 'description': None, 'flow_id': '65570266-d027-4f05-b958-9951ae8eb724', 'concurrency_limit': None, 'global_concurrency_limit': None, 'concurrency_options': None, 'paused': False, 'schedules': [ { 'id': 'dd90ed2b-6e0a-4c2a-adbb-64f14817772e', 'created': '2026-03-02T155103.677105Z', 'updated': '2026-03-02T155103.677124Z', 'deployment_id': 'cba46cab-45d7-444a-a9cd-56fcf1ad148f', 'schedule': {'cron': '30 0 * * *', 'timezone': None, 'day_or': True}, 'active': True, 'max_scheduled_runs': None, 'parameters': {}, 'slug': None } ], 'job_variables': {}, 'parameters': {}, 'pull_steps': None, 'tags': [], 'labels': {'prefect.flow.id': '65570266-d027-4f05-b958-9951ae8eb724'}, 'work_queue_name': None, 'last_polled': '2026-03-04T003219.074355Z', 'parameter_openapi_schema': {'title': 'Parameters', 'type': 'object', 'properties': {}}, 'path': '.', 'entrypoint': 'deployments/iit_pre_dbt_syncs.py:pre_dbt_syncs', 'storage_document_id': None, 'infrastructure_document_id': None, 'created_by': None, 'updated_by': None, 'work_queue_id': None, 'enforce_parameter_schema': True, 'work_pool_name': None, 'status': 'READY', 'automations': [] } root@fa6a1e05a279:/opt/project# prefect deployment inspect 'post-dbt-jobs/post_dbt_jobs' { 'id': 'ad0ef257-4669-454a-8458-5adbab012d0f', 'created': '2026-02-20T170540.169718Z', 'updated': '2026-03-02T155057.701000Z', 'name': 'post_dbt_jobs', 'version': '5517ada88c6167ea94157e168f8cbeee', 'version_id': None, 'version_info': None, 'branch': None, 'base': None, 'root': None, 'description': None, 'flow_id': '357b8a05-2b3f-40ec-9efc-f005f62d01d1', 'concurrency_limit': None, 'global_concurrency_limit': None, 'concurrency_options': None, 'paused': False, 'schedules': [ { 'id': '084cf54b-918b-4efc-bf78-69013402f169', 'created': '2026-03-02T155057.747228Z', 'updated': '2026-03-02T155057.747248Z', 'deployment_id': 'ad0ef257-4669-454a-8458-5adbab012d0f', 'schedule': {'cron': '0 4 * * *', 'timezone': None, 'day_or': True}, 'active': True, 'max_scheduled_runs': None, 'parameters': {}, 'slug': None } ], 'job_variables': {}, 'parameters': {}, 'pull_steps': None, 'tags': [], 'labels': {'prefect.flow.id': '357b8a05-2b3f-40ec-9efc-f005f62d01d1'}, 'work_queue_name': None, 'last_polled': '2026-03-04T002942.794769Z', 'parameter_openapi_schema': {'title': 'Parameters', 'type': 'object', 'properties': {}}, 'path': '.', 'entrypoint': 'deployments/iit_post_dbt_jobs.py:post_dbt_jobs', 'storage_document_id': None, 'infrastructure_document_id': None, 'created_by': None, 'updated_by': None, 'work_queue_id': None, 'enforce_parameter_schema': True, 'work_pool_name': None, 'status': 'READY', 'automations': [] } root@fa6a1e05a279:/opt/project# prefect deployment schedule ls 'pre-dbt-syncs/pre_dbt_syncs' Deployment Schedules ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓ ┃ ID ┃ Schedule ┃ Active ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩ │ dd90ed2b-6e0a-4c2a-adbb-64f14817772e │ cron: 30 0 * * * │ True │ └──────────────────────────────────────┴──────────────────┴────────┘ root@fa6a1e05a279:/opt/project# prefect deployment schedule ls 'post-dbt-jobs/post_dbt_jobs' Deployment Schedules ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━┓ ┃ ID ┃ Schedule ┃ Active ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━┩ │ 084cf54b-918b-4efc-bf78-69013402f169 │ cron: 0 4 * * * │ True │ └──────────────────────────────────────┴─────────────────┴────────┘ root@fa6a1e05a279:/opt/project#
m
thought for 236.6 seconds
Thanks for sharing those outputs — a few key observations: - Both deployments are not paused and their schedules are active. - The UI shows no “Next run”, which only happens when there are no future SCHEDULED flow runs in the DB for that deployment. - Both deployments show last_polled around 2026-03-04 — that’s stale. It suggests the serve runner may not be polling these deployments anymore (or that field isn’t being updated by your runner), but importantly: the “Next run” in the UI does not depend on the runner — it depends on the server-side scheduler generating future runs. So the scheduler is not generating runs for just these two schedules right now. Fastest next steps to confirm and fix 1) Confirm there are truly no future scheduled runs - This will tell us if the scheduler has stopped generating for these deployments.
Copy code
prefect flow-run ls --flow-name pre_dbt_syncs --state SCHEDULED --limit 10
prefect flow-run ls --flow-name post_dbt_jobs --state SCHEDULED --limit 10
If you get 0 results for both, it matches the UI and confirms the scheduler hasn’t generated any future runs. 2) Nudge the scheduler to regenerate for these two deployments - Toggling the schedules forces a re-evaluation by the server’s scheduler services.
Copy code
prefect deployment schedule pause 'pre-dbt-syncs/pre_dbt_syncs' --all
prefect deployment schedule resume 'pre-dbt-syncs/pre_dbt_syncs' --all

prefect deployment schedule pause 'post-dbt-jobs/post_dbt_jobs' --all
prefect deployment schedule resume 'post-dbt-jobs/post_dbt_jobs' --all
- Give it 60–120 seconds and refresh the UI. You should see “Next run” reappear if this was a stale scheduling state. 3) If the toggle does not restore “Next run” - Re-apply the schedules by re-running the serve code (or restart each serve container so it calls
RunnerDeployment.apply()
again). This re-syncs the schedule definition and makes it “recent” for the server’s “recent deployments scheduler” loop to pick up quickly. - Alternatively, temporarily add a minute-level schedule to test whether the scheduler will generate runs now, then remove it:
Copy code
prefect deployment schedule create 'pre-dbt-syncs/pre_dbt_syncs' --cron "*/5 * * * *"
# wait ~1–2 minutes and check UI, then:
prefect deployment schedule delete 'pre-dbt-syncs/pre_dbt_syncs' <that_new_schedule_id>
4) Double-check the serve runners are healthy - The stale last_polled (2026-03-04) is suspicious. Please confirm the two serve containers are still running and actively polling. Typical runner logs will show periodic “polling for scheduled runs” messages. If they’ve stopped, they won’t execute new runs even if the server starts scheduling them again. That said, even with a stopped runner, the UI should still show a “Next run” if the server is generating future runs — so a missing “Next run” points squarely at scheduling, not execution. 5) If issues persist, grab scheduler logs from the server container - Set server logging to debug and look for entries about scheduling these deployment IDs: - Look for lines mentioning “scheduler” and either of these IDs: - pre_dbt_syncs deployment_id: cba46cab-45d7-444a-a9cd-56fcf1ad148f - post_dbt_jobs deployment_id: ad0ef257-4669-454a-8458-5adbab012d0f - Also confirm the scheduler service is enabled (it should be, since other deployments are fine). Optional hardening for the future - If these containers are ever shut down or restarted, consider running serve with
pause_on_shutdown=False
so schedules keep generating even if the runner is offline:
Copy code
pre_dbt_syncs.serve(name="pre_dbt_syncs", cron="30 0 * * *", pause_on_shutdown=False)
post_dbt_jobs.serve(name="post_dbt_jobs", cron="0 4 * * *", pause_on_shutdown=False)
- Or add a small startup step that resumes schedules for these two deployments on container start. Could you: - Run the two
flow-run ls
commands above and share the output? - Try the pause/resume toggle and let me know if “Next run” reappears within a couple minutes?
If that doesn’t resolve it, we’ll dig into the server scheduler logs next.
p
@Marvin I'm unsure if it is the pause_on_shutdown hypothesis since the inspect command of the deployment showed that it was active and the schedule was True. Last polled being 3 days before the last run seems to me that something went wrong there. I toggled my deployment schedule off and on. After waiting a few minutes the UI did not create any scheduled runs under 'Upcoming'. Perhaps the creation of scheduled runs relies on polling isn't that frequent? I also did this toggle on a deployment that had a working schedule and they also did not materialise. I believe I have sorted the problem, and i've just restarted the whole stack of prefect server and deployments, now I see the next 3 scheduled runs under 'upcoming' on each deployment. My next question is: "Are the maximum number of scheduled runs only ever 3? and when the deployment polls the scheduler do more appear on a 3-run rolling basis?"
m
thought for 118.3 seconds
Great to hear a full restart brought “Upcoming” back — that strongly points to the server-side scheduler state getting “stuck” and being reset on restart. Answer to your question on “3 upcoming runs” - By default, Prefect’s server-side scheduler targets at least 3 upcoming auto-scheduled runs per schedule, and no more than 100 runs per batch, while also staying within a time horizon. - So yes: seeing 3 in Upcoming is expected with defaults. As time passes (and as the scheduler loop runs every ~60s by default), it keeps that window “topped up” — effectively a rolling basis. Defaults that control this behavior - min_runs = 3 - max_runs = 100 - min_scheduled_time = PT1H (1 hour) - max_scheduled_time = P100D (100 days) - loop_seconds = 60 - recent_deployments_loop_seconds = 5 How it works - The server’s scheduler periodically checks each active schedule. If there are fewer than min_runs future SCHEDULED flow runs, or if the furthest SCHEDULED run is sooner than min_scheduled_time from “now”, it generates more. - It won’t schedule beyond max_scheduled_time into the future, and it won’t make more than max_runs in one go. - The runner/serve process does not create the schedule — it only polls for and executes runs that the server already created. The “Upcoming” list in the UI strictly reflects what the server scheduled. If you want to see more than 3 in Upcoming - Increase min_runs and/or min_scheduled_time on the server. You can set these via env vars or your Prefect config: - PREFECT_SERVER_SERVICES_SCHEDULER_MIN_RUNS - PREFECT_SERVER_SERVICES_SCHEDULER_MIN_SCHEDULED_TIME - PREFECT_SERVER_SERVICES_SCHEDULER_MAX_SCHEDULED_TIME - Example (env): - PREFECT_SERVER_SERVICES_SCHEDULER_MIN_RUNS=10 - PREFECT_SERVER_SERVICES_SCHEDULER_MIN_SCHEDULED_TIME=PT6H - Then restart the server so the scheduler picks up the new settings. - Verify your current values:
Copy code
prefect config view --show-defaults --show-sources
Notes on your earlier toggle not materializing runs - Your restart fixing it suggests the scheduler service loop had stopped processing certain schedules (even though it was running for others). Toggling “active” on/off should nudge the “recent deployments” scheduler to regenerate; if it doesn’t, a server restart is the definitive reset. - If this happens again, capturing the server logs around the scheduler service would help us isolate whether there’s an edge case we should file. If you want, share the output of: -
prefect config view --show-defaults --show-sources | grep -i scheduler -n
and I can suggest concrete values to tune how many Upcoming runs you see and how far ahead they’re generated.