Skip to content

feat(server): support MEM0_CONFIG_PATH env var for K8s ConfigMap-driven config#5338

Open
shivamtyagi18 wants to merge 1 commit into
mem0ai:mainfrom
shivamtyagi18:feat/mem0-config-path-env
Open

feat(server): support MEM0_CONFIG_PATH env var for K8s ConfigMap-driven config#5338
shivamtyagi18 wants to merge 1 commit into
mem0ai:mainfrom
shivamtyagi18:feat/mem0-config-path-env

Conversation

@shivamtyagi18
Copy link
Copy Markdown

@shivamtyagi18 shivamtyagi18 commented Jun 1, 2026

Summary

Closes #5339 (first half — MEM0_CONFIG_PATH; Docker Hub publishing half tracked separately on the issue)

Adds env-var indirection to the mem0 server's config-loading path so a deployment can mount a YAML config file from a Kubernetes ConfigMap (or any other declarative source) without baking config into the image.

  • New: _load_yaml_config_path() in server/server_state.py — reads MEM0_CONFIG_PATH env var at startup, parses the referenced YAML, merges it into the running config via the existing _merge_config helper.
  • Wiring: called inside initialize_state() before DB overrides apply.
  • Dependency: pyyaml>=6.0,<7.0 added to server/requirements.txt (lazy-imported inside the function — no impact when the env var is unset).
  • Tests: 8 new tests in server/tests/test_mem0_config_path.py covering env-set and env-unset paths, 3 error-fallback paths, and 2 initialize_state integration tests. All pass in ~0.02s.

Motivation

Deploying mem0 to Kubernetes today requires one of:

  1. Bake the config into a custom image build — every config change is a rebuild + redeploy.
  2. Call POST /configure on every pod restart — fragile, racy, and requires custom orchestration outside the K8s declarative model.

The standard 12-factor pattern is a ConfigMap mounted at a stable path, with the location passed via an env var. This PR enables that:

# Kubernetes pod spec excerpt
env:
- name: MEM0_CONFIG_PATH
  value: /app/config/mem0-config.yaml
volumeMounts:
- name: config
  mountPath: /app/config
  readOnly: true
# (with the ConfigMap declaring the YAML separately)

After this PR, the operator edits the ConfigMap → kubectl rolls the pod → server picks up the new config at startup. No image rebuild, no runtime POST.

Design notes

  • Precedence (low → high): DEFAULT_CONFIG < MEM0_CONFIG_PATH < DB overrides. Matches the existing layered-overrides model.
  • Behavior unchanged when MEM0_CONFIG_PATH is unset. Pure additive — no regressions for existing users.
  • Fail-soft on all error paths (missing file, invalid YAML, non-mapping top-level). Matches the style of the existing _load_overrides function: operator misconfiguration produces clear ERROR logs (with exc_info=True for parse failures) but the server still boots on defaults rather than crash-looping. Same trade-off the surrounding code already makes; I considered failing fast to prevent silent-wrong-config but chose consistency with the file's existing style. Happy to switch to fail-fast if maintainers prefer.
  • pyyaml import is lazy — only triggered when MEM0_CONFIG_PATH is actually set. If pyyaml is missing the function logs an actionable ERROR and falls back to defaults rather than crashing imports.

Test coverage

The new test file covers:

  • Env var unset → returns {} (no-op)
  • Env var set to valid YAML file → returns parsed dict
  • Env var set but file missing → logs ERROR + returns {}
  • Env var set but YAML invalid → logs ERROR + returns {}
  • Env var set but YAML top-level is a list/string (not a dict) → logs ERROR + returns {}
  • initialize_state() called with no env var → identical behavior to current main
  • initialize_state() called with env var → loaded config is merged

Uses monkeypatched mem0.Memory to keep test runtime fast (~0.02s for the full file).

Out of scope (intentional)

  • No changes to the public HTTP API. POST /configure remains the runtime-config path; MEM0_CONFIG_PATH is the startup-config path.
  • No changes to server/Dockerfile, docker-compose.yml, or the existing cd.yml PyPI publish workflow.
  • No new env vars beyond MEM0_CONFIG_PATH.

Test plan

  • All 8 new tests pass locally against current main (python3 -m pytest server/tests/test_mem0_config_path.py -q)
  • pyyaml import is lazy and only triggers when env var is set
  • Author email is my personal address (this is an upstream contribution, no employer involvement)
  • Maintainer review

Companion issue

I've also filed an issue describing the broader K8s deployment gaps I hit (this PR addresses the MEM0_CONFIG_PATH half; the other half is the Docker Hub image publishing cadence). Happy to discuss approach changes there before further work.


Thanks for mem0 — the cross-thread recall semantics are exactly what we needed and the server is rock-solid. This patch came out of running mem0 in production K8s; the only friction point was config indirection. Hope this is useful.

…en config

Adds env-var indirection to the server's config-loading path so the
deployment can mount a YAML config file from a Kubernetes ConfigMap
(or any other declarative source) without baking config into the
image.

Behavior:
- If MEM0_CONFIG_PATH is set, the YAML file at that path is parsed
  and merged into the running config via the existing _merge_config
  helper before DB overrides apply.
- If MEM0_CONFIG_PATH is unset, behavior is unchanged from current
  main — pure additive.

Precedence (low → high): DEFAULT_CONFIG < MEM0_CONFIG_PATH < DB.

Motivation: deploying mem0 to Kubernetes today requires either a
runtime POST /configure call on every pod restart, or baking the
config into a custom image build. Both are operationally fragile.
The standard 12-factor pattern is a ConfigMap mounted at a stable
path with the location passed via env var — this PR enables that.

Fail-soft on errors (missing file, invalid YAML, non-mapping
top-level) — matches the style of the existing _load_overrides
function. Operator misconfiguration produces clear ERROR logs but
the server still boots on defaults rather than crash-looping. Same
trade-off the surrounding code already makes.

Files:
- server/server_state.py — new _load_yaml_config_path() function +
  call site inside initialize_state(); errors logged with exc_info=True
- server/requirements.txt — adds pyyaml>=6.0,<7.0 (only imported
  lazily inside _load_yaml_config_path; no impact when env var unset)
- server/tests/__init__.py — new empty test package
- server/tests/test_mem0_config_path.py — 8 tests covering env-set
  and env-unset paths, 3 error-fallback paths, and 2 initialize_state
  integration tests (uses monkeypatched mem0.Memory stub for fast
  execution, ~0.02s for the full file)

Tests pass with the existing pytest setup. No changes to the public
HTTP API.
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Jun 1, 2026

CLA assistant check
All committers have signed the CLA.

@shivamtyagi18
Copy link
Copy Markdown
Author

Note: the companion issue was refiled as #5339 (from my personal account, for OSS-identity consistency). Old #5337 is closed as a duplicate. PR body updated to reference the new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Production K8s deployment: env-driven config + tagged Docker image publishing

2 participants