Core Concepts
Configuration
Settings, environment variables and observability.
CronConfig
Every setting is read from the environment when CronConfig() is constructed, and can be overridden in code afterwards.
from fastapi_crons import Crons, CronConfig
config = CronConfig()
config.sqlite_db_path = "var/cron_state.db"
config.enable_distributed_locking = True
crons = Crons(app, config=config)Construct the config before the scheduler
Crons() reads the config once, at construction, to build the default state backend and lock manager. Mutating it afterwards will not retroactively change those.
Environment variables
The full set of variables, with their defaults.
General
| Variable | Default | Description |
|---|---|---|
CRON_INSTANCE_ID | random 8 chars | Identifies this scheduler instance in state and locks. |
CRON_SQLITE_DB_PATH | cron_state.db | Path to the SQLite state database. |
CRON_LOG_LEVEL | INFO | Logger level. |
CRON_ENABLE_JOB_LOGGING | true | Log each job start and finish. |
Redis
| Variable | Default | Description |
|---|---|---|
CRON_REDIS_URL | unset | Full connection URL. Takes precedence over the host and port settings. |
CRON_REDIS_HOST | localhost | Redis host. |
CRON_REDIS_PORT | 6379 | Redis port. |
CRON_REDIS_DB | 0 | Redis database number. |
CRON_REDIS_PASSWORD | unset | Redis password. |
Locking
| Variable | Default | Description |
|---|---|---|
CRON_ENABLE_DISTRIBUTED_LOCKING | false | Turn on distributed locking. Accepts true, 1 or yes. |
CRON_LOCK_TTL | 300 | Seconds before an abandoned lock expires. |
Retries and timeouts
| Variable | Default | Description |
|---|---|---|
CRON_DEFAULT_MAX_RETRIES | 0 | Retry attempts for jobs that do not set their own. |
CRON_DEFAULT_RETRY_DELAY | 1.0 | Initial delay in seconds. |
CRON_RETRY_BACKOFF_MULTIPLIER | 2.0 | Exponential backoff factor. |
CRON_MAX_RETRY_DELAY | 300.0 | Ceiling on any single backoff delay. |
CRON_DEFAULT_JOB_TIMEOUT | unset | Default job timeout in seconds. No timeout when unset. |
Set CRON_INSTANCE_ID in production
It defaults to a random value generated at startup, so a restarted replica gets a new identity. Pin it to something stable — the pod or hostname — to keep lock ownership and state attribution readable.
Inspecting the active config
python -m fastapi_crons.cli config-showOpenTelemetry
Install the otel extra, then register the tracing hooks. Each run becomes a span carrying the job name, tags and outcome.
from fastapi_crons import OpenTelemetryHooks, is_otel_available
if is_otel_available():
otel = OpenTelemetryHooks(service_name="my-app-crons")
crons.add_before_run_hook(otel.before_run)
crons.add_after_run_hook(otel.after_run)
crons.add_on_error_hook(otel.on_error)is_otel_available() returns False when the extra is not installed, so the block above is safe to leave in code that ships without it.
Thread safety
Sync jobs run in a thread pool while async jobs share the event loop. The scheduler itself is safe to use from both, but your job bodies are not automatically protected — guard any shared mutable state you touch, and use a connection pool rather than a single shared database connection.