Reference
FAQ
Short answers to the questions that come up most.
Will my jobs run twice if I scale to two replicas?
Yes. The scheduler runs in your app process, so each replica schedules every job. Enable distributed locking so only one replica executes each tick.
What happens to runs missed while the app was down?
They are skipped. The scheduler computes the next run from the current time on startup and does not replay missed ticks. If a job must not be missed, make it idempotent and have it work out what it still needs to do from your own data.
Can one job overlap with itself?
No. Each job has its own loop, and the next tick is only scheduled once the previous run finishes. A run that overruns its interval causes ticks to be skipped, not stacked.
Which time zone are schedules in?
The local time zone of the process. Set TZ explicitly so every replica agrees — see Cron Expressions.
How do I test a job?
The decorator returns the function unchanged, so call it directly in a test. For scheduling behaviour, construct Crons() without an app and inspect get_jobs().
Can I run the scheduler separately from the API?
Yes. python -m fastapi_crons.cli start-scheduler runs the loops without a web server. Deploy it as its own process and leave Crons(app) out of your API replicas — see CLI.
Do I need to install anything extra for the dashboard?
No. The bundle ships inside the package, so a plain pip install fastapi-crons can serve it. The dashboard extra is kept as an empty extra so older install commands keep working — see Dashboard.
Why does __version__ not match the version I installed?
The release automation updates pyproject.toml but not fastapi_crons/__init__.py, so the attribute and the health endpoint can report an older number. pip show fastapi-crons is authoritative.
Which state backend should I use?
SQLite for a single instance or local development. Redis or one of the SQL backends once you run more than one replica — SQLite does not handle concurrent writers well. See State Backends.
Can a job take arguments?
No. Jobs are called with no arguments, because the scheduler has nothing to pass them. Close over what you need, or read it from configuration inside the job.
Still stuck?
Open an issue on GitHub.