Environment Variables
herdctl supports environment variable interpolation in configuration files, allowing you to inject secrets and environment-specific values without hardcoding them.
Interpolation Syntax
Section titled “Interpolation Syntax”Environment variables can be referenced in any string value within your configuration using the ${VAR_NAME} syntax.
Required Variables
Section titled “Required Variables”Use ${VAR_NAME} to reference a required environment variable:
mcp: servers: - name: github env: GITHUB_TOKEN: ${GITHUB_TOKEN}If the variable is not defined, herdctl will throw an error at configuration load time:
UndefinedVariableError: Undefined environment variable 'GITHUB_TOKEN' at 'mcp.servers[0].env.GITHUB_TOKEN' (no default provided)Variables with Default Values
Section titled “Variables with Default Values”Use ${VAR_NAME:-default} to provide a fallback value:
workspace: root: ${HERDCTL_WORKSPACE_ROOT:-~/herdctl-workspace}
defaults: model: ${CLAUDE_MODEL:-claude-sonnet-4-20250514}If HERDCTL_WORKSPACE_ROOT is not set, the value ~/herdctl-workspace will be used instead.
Where Interpolation Works
Section titled “Where Interpolation Works”Environment variable interpolation works on any string value in your configuration, at any nesting depth:
# Top-level stringsversion: 1
fleet: name: ${FLEET_NAME:-production} description: Fleet for ${ENVIRONMENT:-development} environment
# Nested in objectsworkspace: root: ${WORKSPACE_ROOT:-~/herdctl}
# Inside arraysagents: - path: ./agents/${AGENT_PROFILE:-default}.yaml - path: ${CUSTOM_AGENT_PATH}
# Deeply nesteddefaults: permissions: bash: allowed_commands: - ${PACKAGE_MANAGER:-npm}
# MCP server configurationmcp: servers: - name: github env: GITHUB_TOKEN: ${GITHUB_TOKEN} API_URL: ${GITHUB_API_URL:-https://api.github.com}Non-String Values
Section titled “Non-String Values”Interpolation only applies to strings. Non-string values (numbers, booleans, null) are preserved as-is:
defaults: max_turns: 50 # Number - no interpolation docker: enabled: true # Boolean - no interpolation
workspace: clone_depth: 1 # Number - no interpolation auto_clone: true # Boolean - no interpolationMultiple Variables in One String
Section titled “Multiple Variables in One String”You can use multiple variables in a single string value:
fleet: description: "${TEAM_NAME} fleet in ${ENVIRONMENT}"
workspace: root: ${HOME}/${PROJECT_NAME:-herdctl}/workspaceError Behavior
Section titled “Error Behavior”Undefined Required Variables
Section titled “Undefined Required Variables”When a required variable (without a default) is undefined, configuration loading fails immediately with a descriptive error:
UndefinedVariableError: Undefined environment variable 'API_KEY' at 'mcp.servers[0].env.API_KEY' (no default provided)The error message includes:
- The variable name that’s missing
- The full path in the configuration where it was referenced
- A reminder that no default was provided
Validation Timing
Section titled “Validation Timing”Environment variables are resolved when the configuration is loaded, not when it’s parsed. This means:
- YAML syntax is validated first
- Schema validation runs second
- Environment variable interpolation happens third
If interpolation fails, you’ll see the UndefinedVariableError after YAML and schema validation pass.
Security Recommendations
Section titled “Security Recommendations”Never Commit Secrets
Section titled “Never Commit Secrets”Keep sensitive values out of version control:
.env.env.local.env.*.localUse Environment Variables for All Secrets
Section titled “Use Environment Variables for All Secrets”Always use interpolation for sensitive values:
# Good: Secrets come from environmentmcp: servers: - name: github env: GITHUB_TOKEN: ${GITHUB_TOKEN} - name: anthropic env: ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
webhooks: secret_env: WEBHOOK_SECRET # Reference by name, not value# Bad: Secrets hardcoded in configmcp: servers: - name: github env: GITHUB_TOKEN: ghp_xxxxxxxxxxxx # Never do this!Set Variables in Your Environment
Section titled “Set Variables in Your Environment”Export variables in your shell or use a secrets manager:
# In your shell profile (~/.bashrc, ~/.zshrc)export ANTHROPIC_API_KEY="sk-ant-..."export GITHUB_TOKEN="ghp_..."
# Or set them when running herdctlANTHROPIC_API_KEY="sk-ant-..." herdctl run coderProduction Environments
Section titled “Production Environments”For production deployments, use your platform’s secrets management:
- CI/CD: Use GitHub Actions secrets, GitLab CI variables, etc.
- Kubernetes: Use Kubernetes Secrets or external secrets operators
- Cloud: Use AWS Secrets Manager, GCP Secret Manager, Azure Key Vault
- Docker: Use Docker secrets or environment files
Common Patterns
Section titled “Common Patterns”API Tokens and Keys
Section titled “API Tokens and Keys”mcp: servers: - name: github env: GITHUB_TOKEN: ${GITHUB_TOKEN}
- name: linear env: LINEAR_API_KEY: ${LINEAR_API_KEY}
# Chat integrations reference env vars by namechat: discord: enabled: true token_env: DISCORD_BOT_TOKEN # Name of the env var, not the valueURLs and Endpoints
Section titled “URLs and Endpoints”mcp: servers: - name: custom-api env: API_BASE_URL: ${API_BASE_URL:-https://api.example.com} API_VERSION: ${API_VERSION:-v1}Paths and Directories
Section titled “Paths and Directories”workspace: root: ${HERDCTL_WORKSPACE:-~/herdctl-workspace}
agents: - path: ${AGENTS_DIR:-./agents}/coder.yaml - path: ${AGENTS_DIR:-./agents}/reviewer.yamlEnvironment-Specific Configuration
Section titled “Environment-Specific Configuration”fleet: name: ${FLEET_NAME:-development} description: ${FLEET_DESCRIPTION:-Local development fleet}
defaults: model: ${CLAUDE_MODEL:-claude-sonnet-4-20250514}
# Different settings per environment instances: max_concurrent: ${MAX_CONCURRENT_AGENTS:-2}Dynamic Agent Selection
Section titled “Dynamic Agent Selection”agents: - path: ./agents/${AGENT_PROFILE:-standard}.yamlSet AGENT_PROFILE=advanced to load ./agents/advanced.yaml instead of the default.
Core Environment Variables
Section titled “Core Environment Variables”While herdctl doesn’t require specific environment variables, these are commonly used:
| Variable | Description | Example |
|---|---|---|
ANTHROPIC_API_KEY | Claude API key for agent sessions | sk-ant-... |
GITHUB_TOKEN | GitHub API token for MCP server | ghp_... |
LINEAR_API_KEY | Linear API key for issue tracking | lin_api_... |
DISCORD_BOT_TOKEN | Discord bot token for chat integration | — |
SLACK_BOT_TOKEN | Slack bot token for chat integration | — |
Complete Example
Section titled “Complete Example”Here’s a full configuration demonstrating environment variable usage:
version: 1
fleet: name: ${FLEET_NAME:-production} description: ${FLEET_DESCRIPTION:-Agent fleet for ${TEAM_NAME:-engineering}}
defaults: model: ${CLAUDE_MODEL:-claude-sonnet-4-20250514} max_turns: ${MAX_TURNS:-50}
permissions: mode: acceptEdits bash: allowed_commands: - ${PACKAGE_MANAGER:-npm} - git - node
workspace: root: ${HERDCTL_WORKSPACE:-~/herdctl-workspace} auto_clone: true clone_depth: 1
agents: - path: ./agents/${AGENT_PROFILE:-default}/coder.yaml - path: ./agents/${AGENT_PROFILE:-default}/reviewer.yaml
mcp: servers: - name: github env: GITHUB_TOKEN: ${GITHUB_TOKEN} - name: filesystem env: ALLOWED_PATHS: ${ALLOWED_PATHS:-/tmp}
chat: discord: enabled: ${DISCORD_ENABLED:-false} token_env: DISCORD_BOT_TOKEN
webhooks: enabled: true port: ${WEBHOOK_PORT:-8081} secret_env: WEBHOOK_SECRETTroubleshooting
Section titled “Troubleshooting””Undefined environment variable” Error
Section titled “”Undefined environment variable” Error”Problem: Configuration fails to load with UndefinedVariableError.
Solution: Either set the missing variable or provide a default:
# Option 1: Set the variableexport MISSING_VAR="value"
# Option 2: Add a default in config# Change: ${MISSING_VAR}# To: ${MISSING_VAR:-default_value}Variable Not Being Replaced
Section titled “Variable Not Being Replaced”Problem: The ${VAR} syntax appears in the final config instead of the value.
Possible causes:
- Variable name contains invalid characters (must match
[A-Za-z_][A-Za-z0-9_]*) - Malformed syntax (missing
}, extra spaces) - Value is not a string type in YAML
# Invalid variable names${123_VAR} # Can't start with number${VAR-NAME} # Hyphens not allowed (use underscores)${VAR NAME} # Spaces not allowed
# Valid variable names${VAR_NAME}${_PRIVATE_VAR}${myVar123}Testing Configuration
Section titled “Testing Configuration”Validate your configuration before running:
# Check that all required variables are setherdctl config validate
# See the resolved configurationherdctl config showRelated
Section titled “Related”- Fleet Configuration - Complete fleet configuration reference
- Agent Configuration - Agent-specific settings
- MCP Servers - MCP server configuration