Summary
BackgroundTasksSettings is documented as no longer reading environment variables at construction. It still does — but under unprefixed names (broker_url, result_backend, retention_days, max_retries, …), because the class subclasses pydantic-settings BaseSettings without an env_prefix.
The result is the opposite of the docstring's intent: instead of reading SM_BG_TASKS_*, it reads a set of very generic names that are easy to collide with in a container, and those silently win over the DB-hydrated values the module is supposed to be configured by.
The mismatch
background_tasks/settings.py opens with:
Construction no longer reads SM_BG_TASKS_* environment variables. Values come from pydantic defaults at boot, then get hydrated from the DB by the hosting lifespan before module on_startup runs.
but declares:
classBackgroundTasksSettings(BaseSettings):
model_config=SettingsConfigDict(extra="ignore")
broker_url: str=Field(default=DEFAULT_BROKER_URL, json_schema_extra=_CELERY_RESTART)
result_backend: str=Field(default=DEFAULT_RESULT_BACKEND, json_schema_extra=_CELERY_RESTART)
...
With no env_prefix, pydantic-settings resolves each field from the bare, case-insensitive field name. So broker_url, result_backend, task_default_queue, stuck_after_seconds, purge_interval_seconds, retention_days and max_retries are all live environment reads.
There's an internal inconsistency too: one field does read a prefixed name explicitly —
task_always_eager: bool=env_bool("SM_BG_TASKS_TASK_ALWAYS_EAGER")— so a single class mixes one SM_BG_TASKS_-prefixed field with nine unprefixed ones.
Confirmed behaviour
Booting a host with SM_ENVIRONMENT=staging and no overrides fails the production guard, as expected:
ValidationError: 1 validation error for BackgroundTasksSettings
Value error, broker_url, result_backend must not point at localhost when
SM_ENVIRONMENT='staging'.
Adding bare entries to .env (which the host loads into os.environ) makes it pass:
broker_url=redis://redis:6379/8
result_backend=redis://redis:6379/9
Adding the documented SM_BG_TASKS_BROKER_URL / SM_BG_TASKS_RESULT_BACKEND instead does not satisfy it — those names reach seed_dev_settings.py and the worker's _assert_broker_isolated, but never the settings class itself.
Why it matters in practice
A production deployment of a downstream app (Dokploy) crash-looped on this validator. The fix that actually worked was setting bare broker_url / result_backend env vars on the container — i.e. depending on this accidental behaviour, under names generic enough that another component setting broker_url for its own purposes would silently reconfigure Celery.
It also makes the DB the source of truth only when nobody happens to have those names in the environment, which is hard to reason about from the outside.
The validator message compounds it: it says "Set these to the Redis service host (e.g. redis://redis:6379/0)" without naming a mechanism, so the natural first guess is the documented SM_BG_TASKS_* prefix, which has no effect.
Suggested fix
Pick whichever matches the intent:
- If env should be ignored (what the docstring says): stop inheriting env parsing for these fields, or set
model_config = SettingsConfigDict(extra="ignore", env_prefix="SM_BG_TASKS_") so at least the names are namespaced and match the docs, the seed script, and _assert_broker_isolated. - If env should be honoured: make it
SM_BG_TASKS_-prefixed for every field, and drop the one-off env_bool on task_always_eager so the class has a single rule.
Either way, it would help if the localhost validator named the mechanism it expects — settings row, or a specific env var — since that error is most people's first encounter with this.
Version
simple_module_background_tasks 0.0.32.
Summary
BackgroundTasksSettingsis documented as no longer reading environment variables at construction. It still does — but under unprefixed names (broker_url,result_backend,retention_days,max_retries, …), because the class subclasses pydantic-settingsBaseSettingswithout anenv_prefix.The result is the opposite of the docstring's intent: instead of reading
SM_BG_TASKS_*, it reads a set of very generic names that are easy to collide with in a container, and those silently win over the DB-hydrated values the module is supposed to be configured by.The mismatch
background_tasks/settings.pyopens with:but declares:
With no
env_prefix, pydantic-settings resolves each field from the bare, case-insensitive field name. Sobroker_url,result_backend,task_default_queue,stuck_after_seconds,purge_interval_seconds,retention_daysandmax_retriesare all live environment reads.There's an internal inconsistency too: one field does read a prefixed name explicitly —
— so a single class mixes one
SM_BG_TASKS_-prefixed field with nine unprefixed ones.Confirmed behaviour
Booting a host with
SM_ENVIRONMENT=stagingand no overrides fails the production guard, as expected:Adding bare entries to
.env(which the host loads intoos.environ) makes it pass:Adding the documented
SM_BG_TASKS_BROKER_URL/SM_BG_TASKS_RESULT_BACKENDinstead does not satisfy it — those names reachseed_dev_settings.pyand the worker's_assert_broker_isolated, but never the settings class itself.Why it matters in practice
A production deployment of a downstream app (Dokploy) crash-looped on this validator. The fix that actually worked was setting bare
broker_url/result_backendenv vars on the container — i.e. depending on this accidental behaviour, under names generic enough that another component settingbroker_urlfor its own purposes would silently reconfigure Celery.It also makes the DB the source of truth only when nobody happens to have those names in the environment, which is hard to reason about from the outside.
The validator message compounds it: it says "Set these to the Redis service host (e.g.
redis://redis:6379/0)" without naming a mechanism, so the natural first guess is the documentedSM_BG_TASKS_*prefix, which has no effect.Suggested fix
Pick whichever matches the intent:
model_config = SettingsConfigDict(extra="ignore", env_prefix="SM_BG_TASKS_")so at least the names are namespaced and match the docs, the seed script, and_assert_broker_isolated.SM_BG_TASKS_-prefixed for every field, and drop the one-offenv_boolontask_always_eagerso the class has a single rule.Either way, it would help if the localhost validator named the mechanism it expects — settings row, or a specific env var — since that error is most people's first encounter with this.
Version
simple_module_background_tasks0.0.32.