Environment Variables
This page is the authoritative reference for every RECOTEM_* environment variable. Variables scoped to train are read only by recotem train; those scoped to serve are read only by recotem serve; those scoped to both are read by both commands.
A value that fails to parse is fatal for some variables and silently ignored for others
Variables marked ⚠ below refuse to start the process: a ConfigError / KeyRingConfigError, exit 8, before the port is bound. Every other numeric variable logs an env_var_unparseable warning and silently falls back to its default, so a typo in e.g. RECOTEM_MAX_PAYLOAD_BYTES leaves the server running with a limit you did not choose. Out-of-range values on a clamped variable are not fatal either — they are clamped, with an env_var_clamped warning. Grep your startup logs for env_var_unparseable and env_var_clamped after any change.
Authentication and signing
These variables control artifact integrity (signing keys) and API authentication. They must be treated as secrets. See Security — Secrets handling for storage recommendations.
| Variable | Default | Scope | Clamping | Description |
|---|---|---|---|---|
⚠ RECOTEM_SIGNING_KEYS | (required) | both | — | kid:hex64,kid2:hex64 — HMAC-SHA256 sign/verify keys (64 hex chars = 32 raw bytes). Multi-entry enables zero-downtime rotation; recotem train always signs with the first entry. A misconfigured or missing value fails closed — there is no unsigned fallback. |
⚠ RECOTEM_API_KEYS | (empty) | serve | — | kid:sha256:hex64,... — API key allow-list. Each entry is a kid paired with a scrypt digest (sha256: is a digest-family label, not the algorithm). Empty value forces the bind address to 127.0.0.1 regardless of RECOTEM_HOST. |
Generating keys
Use recotem keygen --type signing and recotem keygen --type api to generate correctly formatted values for these variables. See Security — recotem keygen output format for the exact output format.
Network binding
These variables control where recotem serve listens for connections.
| Variable | Default | Scope | Clamping | Description |
|---|---|---|---|---|
RECOTEM_HOST | 127.0.0.1 | serve | — | uvicorn bind host. Must be 0.0.0.0 inside Docker or Kubernetes when RECOTEM_API_KEYS is set. Forced back to 127.0.0.1 (with a host_forced_to_loopback warning) when no API keys are configured. |
⚠ RECOTEM_PORT | 8080 | serve | 1–65535 (fatal) | uvicorn bind port. Unlike the other numeric variables the range is enforced rather than clamped: a non-integer or out-of-range value raises ConfigError and exits 8 before the port is bound (RECOTEM_PORT must be in range 1–65535, got 99999). |
RECOTEM_ALLOWED_HOSTS | 127.0.0.1,localhost | serve | — | Comma-separated list passed to TrustedHostMiddleware. Requests with unrecognized Host headers are rejected. Whitespace-only comma input falls back to the default. Set this explicitly in production to the exact hostnames clients will use. |
RECOTEM_ALLOWED_ORIGINS | (empty) | serve | — | Comma-separated CORS allow-list. Empty means deny all cross-origin requests. Set this when browser clients send CORS requests. |
Exposing recotem serve externally
To bind to a non-loopback interface you must configure RECOTEM_API_KEYS. Set RECOTEM_HOST=0.0.0.0, set RECOTEM_ALLOWED_HOSTS to the exact client hostnames, and place a TLS-terminating reverse proxy in front. recotem serve does not terminate TLS.
Limits and caps
These variables control memory, request, and download size limits. The artifact caps are enforced before deserialization.
| Variable | Default | Scope | Clamping | Description |
|---|---|---|---|---|
RECOTEM_MAX_ARTIFACT_BYTES | 2 GiB | serve | [1 MiB, 16 GiB] | Per-artifact file size cap. Enforced before any deserialization occurs. Lowering it below the payload cap is fatal even if you never set the payload cap — the cross-check compares the two resolved values, so RECOTEM_MAX_ARTIFACT_BYTES=268435456 (256 MiB) alone exits 8 naming RECOTEM_MAX_PAYLOAD_BYTES (default 512 MiB), a variable the operator did not set. 512 MiB exactly is accepted. Lower both together, payload first. |
RECOTEM_MAX_PAYLOAD_BYTES | 512 MiB | serve | [1 MiB, 16 GiB] | Per-payload cap applied post-HMAC-verify during deserialization. Must be less than or equal to RECOTEM_MAX_ARTIFACT_BYTES; startup fails with a ConfigError (exit 8) if it is not. Smaller than RECOTEM_MAX_ARTIFACT_BYTES to bound the memory expansion from deserialization. |
RECOTEM_MAX_BODY_BYTES | 128 MiB | serve | [1 MiB, 2 GiB] | Maximum HTTP request body size. A BodySizeLimitMiddleware returns 413 PAYLOAD_TOO_LARGE when the declared Content-Length exceeds the cap, and enforces a running byte count on chunked/streamed bodies with no Content-Length so the header cannot be omitted to bypass it. Enforced before Starlette buffers and JSON-parses the body. |
RECOTEM_MAX_DOWNLOAD_BYTES | 256 MiB | train | [1 MiB, 16 GiB] | Raw I/O bytes cap for HTTP/HTTPS, local file, and object-store source reads. The cap is applied mid-stream; exceeding it raises DataSourceError (exit 3). Does not cap the decompressed DataFrame — see Security — Decompressed-size cap not enforced. |
RECOTEM_MAX_FEATURE_DIM | 5000 | train | [16, 100000] | Cap on the encoded side-feature dimension for feature-aware iALS, checked independently per side (item and user). Exceeding it raises TrainingError (exit 4) at the point the encoder state is built. Vocabulary is built from the whole fetched feature table, so the dimension scales with catalog size, not interaction count; min_frequency is the only recipe-level lever against it. Per-trial time grows super-linearly with an exponent that rises with the dimension (measured: a doubling costs 1.7–1.9× below the default cap, 5.07× from 5,000 and 7.46× from 10,000 — effectively cubic at exactly the step taken when the default cap refuses a catalogue) and memory quadratically, and both multiply with training.parallelism — see Operations — Feature-aware iALS sizing. |
RECOTEM_MAX_FEATURE_DIM is a training-time cap only
It is enforced in build_encoder_state while recotem train builds the encoder, and has no serve-side effect — recotem serve never reads it. Do not treat it as a serving-memory guard: the encoded feature matrix pickled into the artifact scales with n_items × nnz_per_row, which this variable does not bound. Use RECOTEM_MAX_PAYLOAD_BYTES and host sizing for the serve side.
RECOTEM_MAX_BODY_BYTES and the batch verbs
The 128 MiB default clears the largest schema-valid single-verb body (:recommend-related tops out near 52 MiB with maximal cold-start feature mappings) but deliberately not the largest batch body: :batch-recommend tops out near 196 MiB and :batch-recommend-related near 13 GiB — the latter beyond even the 2 GiB clamp. Those are refused with 413. An operator who genuinely sends batches that large must raise the cap.
HTTP fetcher
These variables govern how recotem train fetches http:// and https:// source paths.
| Variable | Default | Scope | Clamping | Description |
|---|---|---|---|---|
RECOTEM_HTTP_TIMEOUT_SECONDS | 30 | train | [1, 600] | Connect and read timeout in seconds for HTTP/HTTPS source fetches. |
RECOTEM_HTTP_ALLOW_PRIVATE | (unset) | train | — | Truthy values: 1, true, yes, on. When set, the HTTP fetcher will connect to private (RFC1918), loopback, and link-local destinations. Leave unset in production to block SSRF attacks against cloud-metadata services (AWS IMDSv1 at 169.254.169.254, GCP metadata at metadata.google.internal). |
WARNING
RECOTEM_HTTP_ALLOW_PRIVATE should never be set in production. Its sole purpose is to support lab environments where the data origin is a trusted internal host. See Security — Operator responsibilities for network sources.
Watcher and startup
These variables control how recotem serve monitors artifact files and loads models at startup.
| Variable | Default | Scope | Clamping | Description |
|---|---|---|---|---|
⚠ RECOTEM_WATCH_INTERVAL | 5 | serve | [1, 30] | Artifact watcher poll interval in seconds. Out-of-range values are clamped, but — unlike every other numeric variable here — a non-numeric value is fatal (ConfigError, exit 8). The watcher detects new or changed artifact files and hot-swaps models without restarting the process. |
RECOTEM_STARTUP_PARALLELISM | (auto) | serve | [1, 32] | Number of parallel threads used to load artifacts at startup. Default auto-sizing is min(len(recipes), 8). Setting to 0 is not a sentinel — it clamps to 1 and emits an env_var_clamped warning. Set to 1 to force sequential loading for debugging. |
Lifecycle
These variables control the runtime environment, graceful shutdown, and log output.
| Variable | Default | Scope | Clamping | Description |
|---|---|---|---|---|
RECOTEM_ENV | (empty) | serve | — | Deployment environment tag. --insecure-no-auth is permitted only when set to development, dev, or test. --dev-allow-unsigned is permitted only when set to development. The /docs, /redoc, and /openapi.json endpoints are fail-secure: they are enabled only when this variable is one of development, dev, or test; for any other value (including unset, production, prod, staging, or a custom tag) those paths return 404. |
RECOTEM_DRAIN_SECONDS | 30 | serve | [1, 300] | SIGTERM graceful drain window in seconds. In-flight requests are given this window to complete before uvicorn closes remaining connections. For Kubernetes, set terminationGracePeriodSeconds to at least RECOTEM_DRAIN_SECONDS + 5. |
⚠ RECOTEM_LOG_FORMAT | auto | both | — | Log output format. Any value other than the three below is fatal (ConfigError, exit 8). auto uses JSON when stderr is not a TTY, console otherwise. json forces structured JSON. console forces human-readable output. |
Operational
These variables configure storage paths, locking, metadata field filtering, and metrics.
| Variable | Default | Scope | Clamping | Description |
|---|---|---|---|---|
RECOTEM_ARTIFACT_ROOT | (empty) | train | — | If set, local output.path values in recipes must lie under this directory. Symlink escapes are rejected. Use this to confine where train processes can write artifacts on the host. |
RECOTEM_LOCK_DIR | (empty) | train | — | Override directory for per-recipe training lock files. Local output.path values always lock at <output_path>.lock. Remote output.path values (s3://, gs://, etc.) require a host-local lock file; if RECOTEM_LOCK_DIR is unset they fall back to <tempdir>/recotem-locks/. Note: flock is host-local — for cross-host single-writer guarantees use scheduler-level mutex (Kubernetes concurrencyPolicy: Forbid, etc.). |
RECOTEM_METADATA_FIELD_DENY | (empty) | serve | — | Comma-separated list of column names dropped from the item-metadata index at load time, so they never appear on any recommendation response (:recommend, :recommend-related, and :batch-recommend* when include_metadata=true). Matching is case-insensitive — "Internal_ID" in the metadata is stripped if "internal_id" is in the deny list. Use this to keep PII columns out of API responses. |
RECOTEM_METRICS_ENABLED | (unset) | serve | — | Truthy values: 1, true, yes, on. Enables the Prometheus /v1/metrics endpoint. Requires the recotem[metrics] extra (pip install "recotem[metrics]"). The endpoint is opt-in and off by default. It requires an API key like every other /v1 route — a scrape without a valid X-API-Key gets 401, so configure the scraper with the key (or run in an unauthenticated posture, which forces the loopback-only bind). The path is /v1/metrics, not /metrics. |
RECOTEM_ALLOW_IRSPACK_VERSION_SKEW | (unset) | serve | — | Truthy values: 1, true, yes, on. Downgrades the serve-side irspack version-skew check from an ArtifactError (recipe stays loaded: false) to an irspack_version_skew_allowed warning, letting the payload reach the deserializer. The default rule is an allow-list: the same irspack major.minor always loads, and a differing major.minor loads only for (best_class, transition) pairs Recotem has empirically verified. Use this only when you know the artifact is unaffected — it does not make an incompatible payload loadable. See Operations — irspack version skew. |
Data source
These variables tune behaviour of specific data sources. They are read only by recotem train and only when the corresponding source is used. See the Data sources reference for full context.
| Variable | Default | Scope | Clamping | Description |
|---|---|---|---|---|
RECOTEM_BQ_REQUIRE_STORAGE_API | (unset) | train | — | Truthy values: 1, true, yes, on. When set, the BigQuery source raises DataSourceError (exit 3) instead of silently falling back to the slower REST API when the BigQuery Storage Read API fails (e.g. missing bigquery.readSessions.create IAM permission). Use this to surface IAM gaps rather than accepting degraded throughput. |
RECOTEM_MAX_SQL_ROWS | 50_000_000 | train | [1_000, 500_000_000] | Hard cap on the number of rows returned by the SQL data source. Exceeding the cap raises DataSourceError (exit 3). Caps row count, not DataFrame resident memory — see SQL source — memory bound caveat. |
RECOTEM_SQL_ALLOW_PRIVATE | (unset) | train | — | Truthy values: 1, true, yes, on. Opts the SQL source into accepting private/loopback DSN hosts (default deny, for SSRF). Covers every driver-routing form — netloc, ?host=, ?hostaddr=, ?service=, ?unix_socket=, absolute-path host, and network DSNs with no host info — all default-deny without this flag. Also disables the DNS-rebinding re-check before each probe/fetch — opting in means trusting the host end-to-end. |
Recipe expansion
Variables with the RECOTEM_RECIPE_ prefix are the only variables eligible for ${...} expansion inside recipe YAML files.
| Variable | Default | Scope | Description |
|---|---|---|---|
RECOTEM_RECIPE_* | — | train | Any variable whose name starts with RECOTEM_RECIPE_ is a candidate for ${VAR_NAME} substitution in recipe fields. A secondary blacklist blocks sensitive names even within this prefix. |
Security constraints on RECOTEM_RECIPE_* expansion
A secondary blacklist refuses expansion of variables whose names match sensitive patterns even if they carry the RECOTEM_RECIPE_ prefix. The blacklist uses exact match, prefix match, and substring match rules — notably, any name containing the substring KEY is rejected. The RECOTEM_RECIPE_ prefix is intended for non-sensitive configuration values such as dataset names, date ranges, partition columns, and feature flags. Never store secrets under this prefix. See Security — Recipe env-var expansion blacklist for the full rules and examples.
