Skip to content

Configuration

Runtime configuration has two sources, in order of precedence:

  1. Environment variables (SM_*) — read at boot, before the DB connection is open. Use for bootstrap plumbing (DB URL, secret key, feature-flag overrides for tests).
  2. DB-backed settings — edited from /settings/modules at runtime. Use for everything else: SMTP creds, storage backends, module-specific toggles.

Most deployments only need SM_DATABASE_URL and SM_SECRET_KEY in the environment — everything else is configurable from the admin UI.

Framework env vars

Prefix is always SM_. These are parsed by simple_module_hosting.settings.Settings (pydantic) at startup.

VariableDefaultNotes
SM_DATABASE_URLsqlite+aiosqlite:///./app.dbRequired in production. Async URL. Postgres: postgresql+asyncpg://…
SM_ENVIRONMENTdevelopmentAny value other than development, test, testing triggers strict discovery + placeholder-secret checks.
SM_SECRET_KEYchange-me-in-productionMust be overridden in production — session cookie signing key.
SM_VITE_DEV_URLhttp://localhost:5050Dev only — where the Vite HMR client connects.
SM_DEBUGfalseEnables debug mode (shows tracebacks in HTTP responses).
SM_LOG_LEVELINFODEBUG/INFO/WARNING/ERROR
SM_LOG_FORMATplainplain for dev, json for structured logs in prod.
SM_MULTI_TENANTfalseEnables TenantMiddleware + MultiTenantMixin auto-filter.
SM_TENANT_HEADERX-Tenant-IDHTTP header that identifies the current tenant.
SM_MODULES_ENABLEDunset (all enabled)Comma-separated allow-list to disable modules without uninstalling them.

Database bootstrap knobs

Only change if you know what you're doing — these must be set before the DB connection opens, so they can't live in the DB-backed settings store.

VariableDefaultNotes
SM_DB_POOL_SIZE5SQLAlchemy pool_size
SM_DB_MAX_OVERFLOW10SQLAlchemy max_overflow
SM_DB_POOL_PRE_PINGtrueTest connections before use
SM_DB_POOL_RECYCLE1800Recycle connections after N seconds

Internationalization

VariableDefaultNotes
SM_I18N_DEFAULT_LOCALEenMust be in SM_I18N_SUPPORTED_LOCALES.
SM_I18N_SUPPORTED_LOCALESenComma-separated, e.g. en,es,de.
SM_I18N_COOKIE_NAMElocaleCookie that stores the user's selected locale.

Users module

Prefix SM_USERS_*. These live in the env for bootstrap; everything else moved to the DB-backed settings store.

VariableDefaultNotes
SM_USERS_BOOTSTRAP_EMAILunsetAuto-creates an admin if set and users_user is empty.
SM_USERS_BOOTSTRAP_PASSWORDunsetPaired with the email above.
SM_USERS_ALLOW_SIGNUPfalseIf true, /users/register is public.
SM_USERS_MAILERconsoleconsole (logs invite link to stdout) or smtp.
SM_USERS_BASE_URLderivedThe public URL used to build invite links.
SM_USERS_SMTP_HOSTOnly when SM_USERS_MAILER=smtp.
SM_USERS_SMTP_PORT587
SM_USERS_SMTP_USERNAME
SM_USERS_SMTP_PASSWORD
SM_USERS_SMTP_FROM
SM_USERS_SMTP_TLStrue

Background tasks (Celery)

Prefix SM_BG_TASKS_*. The defaults in docker-compose.yml already set these so Celery can reach the in-container redis service before DB-backed settings are loaded.

VariableDefaultNotes
SM_BG_TASKS_BROKER_URLredis://localhost:6379/0Celery broker.
SM_BG_TASKS_RESULT_BACKENDredis://localhost:6379/1Celery result backend.

DB-backed settings

After upgrading from an older deployment, import existing SM_* values into the DB settings store once:

bash
uv run sm-settings import-from-env

This is idempotent — it only seeds keys that don't have a DB override yet.

From then on, edit at /settings/modules (requires the settings.manage permission). Changes apply immediately; no restart needed.

Per-module settings convention

When you write your own module, declare settings as a dataclass on app.state.<module_lower> inside register_settings(app). The prefix for env vars must be SM_<MODULE>_*.

python
# modules/orders/orders/settings.py
from dataclasses import dataclass
from pydantic_settings import BaseSettings

class OrdersEnv(BaseSettings):
    max_items_per_order: int = 100
    class Config:
        env_prefix = "SM_ORDERS_"

@dataclass
class OrdersState:
    settings: OrdersEnv

# modules/orders/orders/module.py
class OrdersModule(ModuleBase):
    def register_settings(self, app: FastAPI) -> None:
        app.state.orders = OrdersState(settings=OrdersEnv())

If you override register_settings but don't write to app.state.<module_lower>, diagnostic SM012 warns in dev. See Settings & app.state for details.

Placeholder-secret check

In production (SM_ENVIRONMENT != development), boot fails if SM_SECRET_KEY is still the default change-me-in-production. Override it before deploying.

Released under the MIT License.