Skip to content

Instantly share code, notes, and snippets.

@panchicore
Created February 4, 2026 21:29
Show Gist options
  • Select an option

  • Save panchicore/f65ca1252b4448f4a361656b895eb793 to your computer and use it in GitHub Desktop.

Select an option

Save panchicore/f65ca1252b4448f4a361656b895eb793 to your computer and use it in GitHub Desktop.
VMConfig: Phasing out export_env - migration guide for #infra

VMConfig: Phasing out export_env

Why it exists

Bridge for gradual migration. Lets vmconfig be source of truth while legacy code still reads env vars.

Real example: Our email config has two code paths:

# NEW (vmconfig)
from_addr = vmconfig.get("email.from_addr")

Here's the trick: when infra sets EMAIL_FROM_ADDR env var, vmconfig actually reads from that env var (env vars always win). The export_env bridge works in reverse too — it tells vmconfig "if someone asks for email.from_addr, also check the EMAIL_FROM_ADDR env var."

Servers were already setting EMAIL_FROM_ADDR. We couldn't migrate code and infra simultaneously. So export_env bridges them:

[vmconfig.export_env]
EMAIL_FROM_ADDR = "email.from_addr"  # bidirectional bridge

The problem if we don't finish migrating:

  • Dual maintenance — values in vmconfig AND infra env vars
  • Real env vars override vmconfig silently → "why isn't my change working?"
  • Tech debt accumulates

Migration process: EMAIL_FROM_ADDR example

1. VERIFY code uses vmconfig.get(), not os.getenv()

  • Search: grep -r "EMAIL_FROM_ADDR" src/
  • Found: emails/config.py:254 still uses os.getenv("EMAIL_FROM_ADDR")
  • Migrate: remove that code path, keep only vmconfig.get("email.from_addr")

2. REMOVE from [vmconfig.export_env] in vmconfig.toml

  • Delete: EMAIL_FROM_ADDR = "email.from_addr"

3. CONFIRM value exists in vmconfig.toml

  • Check: [email] from_addr = "no-reply@validmind.ai"

4. UPDATE vmconfig_local.toml.sample if applicable

  • Not needed here (not a secret, default is fine)

5. PUSH code changes

6. SET value on server's vmconfig_local.toml (if override needed)

  • Example: [email] from_addr = "no-reply@prod.validmind.ai"

7. REMOVE env var from infra

  • Delete EMAIL_FROM_ADDR from Terraform/K8s secrets

End state: vmconfig = single source of truth, no env var duplication.


Migration candidates

Ready to migrate (have legacy os.getenv() calls)

Env Var Config Key File with legacy code
EMAIL_SENDER email.provider emails/config.py:211
EMAIL_FROM_ADDR email.from_addr emails/config.py:254
EMAIL_FROM_NAME email.from_name emails/config.py:256
SMTP_SERVER email.smtp.server emails/config.py:216
SMTP_PORT email.smtp.port emails/config.py:220
SMTP_SECURITY email.smtp.security emails/config.py:226
SMTP_USERNAME email.smtp.username emails/config.py:221
POSTMARK_API_KEY email.postmark_api_key emails/config.py:264
SENDGRID_API_KEY email.sendgrid_api_key emails/config.py:266
TOKEN_CACHE_EXPIRATION_TIME_SECONDS api.token_cache_expiration_seconds auth/oidc.py:18
AWS_S3_KMS_KEY_ARN object_storage.aws.kms_key_arn services/object_storage.py:121

Do NOT migrate (external libs read these directly)

Env Var Reason
DD_* Datadog SDK reads from os.environ
AWS_REGION AWS SDK reads from os.environ
AWS_DEFAULT_REGION AWS SDK reads from os.environ

Already clean (no legacy os.getenv() found)

Code is already migrated. Just need to:

  1. Review if default value in vmconfig.toml is appropriate
  2. Decide if it needs to be in vmconfig_local.toml.sample (per-environment or secret)
  3. Remove from [vmconfig.export_env]
  4. Coordinate with infra to remove env var
Env Var Config Key
RUN_MODE api.run_mode
VM_APP_URL api.app_url
VM_API_URL api.api_url
BACKEND_JWT_PRIVATE_KEYS api.jwt_private_keys
UPDATE_THRESHOLD_HOURS api.update_threshold_hours
ENABLE_SIEM_BRIDGE api.enable_siem_bridge
LITELLM_URL litellm.url
LITELLM_MASTER_KEY litellm.master_key
REDIS_URL redis.url
PG_ENCRYPTION_KEY database.encryption_key
AGENTS_MODEL_ALIASES launchdarkly.configure.agents_model_aliases
OBJECT_STORAGE_PROVIDER object_storage.provider
VMPROJECTASSETS_NAME object_storage.bucket_name
OBJECT_STORAGE_PRESIGNED_URL_EXPIRATION object_storage.presigned_url_expiration
OBJECT_STORAGE_WEBHOOK_URL object_storage.webhook_url
SMTP_TIMEOUT email.smtp.timeout

Deprecated (remove with feature)

Env Var Reason
CASBIN_* Casbin is deprecated, will be removed

Transition complete when...

VMConfig is in a transition period. We'll consider it 100% migrated when [vmconfig.export_env] only contains keys required by 3rd party libraries:

[vmconfig.export_env]
# Only these remain — external libs read them directly from os.environ
DD_TRACE_ENABLED = "datadog.trace_enabled"
DD_TRACE_LOG_FORMAT = "datadog.trace_log_format"
DD_TRACE_WRITER_LOG_LEVEL = "datadog.trace_writer_log_level"
DD_TRACE_LOG_LEVEL = "datadog.trace_log_level"
DD_LOGS_INJECTION = "datadog.logs_injection"
DD_TRACE_WRITER_INTERVAL_SECONDS = "datadog.trace_writer_interval_seconds"
DD_TRACE_WRITER_REUSE_CONNECTIONS = "datadog.trace_writer_reuse_connections"
AWS_REGION = "object_storage.aws.region"
AWS_DEFAULT_REGION = "object_storage.aws.region"

Everything else: vmconfig is the single source of truth.

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