CDX-301a · Module 3

Environment-Based Switching

3 min read

Environment-based switching lets you vary Codex behavior across development, staging, and production contexts. The mechanism is simple: environment variables control which config.toml profile activates, which MCP servers connect, and which exec policies apply. In development, Codex gets broad permissions and access to local services. In CI, it gets read-only access and tightly scoped exec policies. In production review, it gets the strictest rules and audit logging.

The environment variable CODEX_PROFILE selects a config.toml profile at startup. Combined with MCP server env blocks that reference shell variables, you can build a config system where the same config.toml file produces different behavior in different environments — without any file swapping or conditional logic in the config itself. The config is declarative; the environment provides the runtime context.

# Environment-driven profile selection
# Set CODEX_PROFILE=dev|staging|ci to activate

[profile.dev]
model = "codex-1"
approval = "suggest"
# Full permissions, local MCP servers

[profile.staging]
model = "codex-1"
approval = "suggest"
# Staging DB access, read-only file system

[profile.ci]
model = "gpt-4.1"
approval = "full-auto"
# No interactive approval, strict sandbox

# MCP servers with environment-scoped secrets
[mcp.database]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-postgres"]
env = { DATABASE_URL = "${DATABASE_URL}" }
# DATABASE_URL differs per environment:
# dev: localhost, staging: staging-db, ci: test-db

Do This

  • Use CODEX_PROFILE to switch between dev, staging, and CI configurations
  • Scope MCP server secrets to the environment via shell variable references
  • Test every profile independently — a config that works in dev may fail in CI
  • Log the active profile at session start for debugging and audit

Avoid This

  • Hardcode database URLs or API keys in config.toml — use ${VARIABLE} references
  • Assume dev and CI profiles are interchangeable — they serve different purposes
  • Forget to test the CI profile locally before pushing — broken CI wastes everyone's time
  • Create environment-specific AGENTS.md files when a profile switch would suffice