graft provides two powerful ways to work with environment variables: using them as literal values and expanding them within reference paths.
The simplest way to use environment variables is with the $ prefix:
database_url: (( grab $DATABASE_URL ))
api_key: (( grab $API_KEY ))
debug_mode: (( grab $DEBUG_ENABLED ))If the environment variable is not set, graft will error. To provide defaults, use the || operator:
port: (( grab $PORT || 8080 ))
environment: (( grab $APP_ENV || "development" ))
log_level: (( grab $LOG_LEVEL || "info" ))A more advanced feature allows environment variables to be expanded within reference paths using the $VAR syntax:
# If ENVIRONMENT="production", this grabs config.production.database
database: (( grab config.$ENVIRONMENT.database ))
# Multiple variables can be used
setting: (( grab $TIER.$REGION.$SERVICE.endpoint ))- Any path segment starting with
$is treated as an environment variable - The variable name extends to the next
.or end of path - If the environment variable is not set, it expands to an empty string
- This works in any operator that accepts references
environments:
development:
api_url: http://localhost:8080
debug: true
staging:
api_url: https://staging.example.com
debug: true
production:
api_url: https://api.example.com
debug: false
# Select config based on APP_ENV
current: (( grab environments.$APP_ENV ))
api_url: (( grab environments.$APP_ENV.api_url ))tenants:
acme:
api_key: abc123
plan: enterprise
globex:
api_key: xyz789
plan: starter
# Use TENANT environment variable
tenant_config: (( grab tenants.$TENANT ))
api_key: (( grab tenants.$TENANT.api_key ))regions:
us-east-1:
ami: ami-12345678
vpc: vpc-abcd1234
eu-west-1:
ami: ami-87654321
vpc: vpc-efgh5678
# AWS_REGION from CI/CD
deployment:
ami: (( grab regions.$AWS_REGION.ami ))
vpc: (( grab regions.$AWS_REGION.vpc ))The logical-or operator provides fallback values:
# Simple default
value: (( grab original.value || "default-value" ))
# Reference as default
value: (( grab primary.value || secondary.value ))
# Multiple fallbacks
value: (( grab primary || secondary || tertiary || "default" ))
# With environment variables
database_host: (( grab $DB_HOST || config.database.host || "localhost" ))# Try multiple sources in order
api_endpoint: ((
grab $API_ENDPOINT_OVERRIDE ||
grab endpoints.$ENVIRONMENT ||
grab endpoints.default ||
"http://localhost:8080"
))# Build paths from multiple variables
base_path: (( concat "services." $NAMESPACE "." $SERVICE ))
endpoint: (( grab (grab base_path).url ))
# Conditional paths
config: (( grab $USE_CUSTOM == "true" ? custom.$PROFILE : defaults ))# With vault
secrets:
path: (( concat "secret/" $ENVIRONMENT "/" $APP_NAME ))
password: (( vault (concat (grab secrets.path) ":password") ))
# With concat
connection_string: (( concat
(grab $DB_TYPE || "postgresql") "://"
(grab $DB_USER || "app") ":"
(grab $DB_PASS || "password") "@"
(grab $DB_HOST || "localhost") ":"
(grab $DB_PORT || "5432") "/"
(grab $DB_NAME || "myapp")
))# Select from lists using environment variables
environments: [dev, staging, prod]
current_env: (( grab environments.[$ENV_INDEX] ))
# Build lists dynamically
allowed_origins:
- (( grab $ORIGIN_1 || "http://localhost:3000" ))
- (( grab $ORIGIN_2 || "http://localhost:8080" ))
- (( grab $ORIGIN_3 || "" ))Always document which environment variables your configuration expects:
# Required Environment Variables:
# - APP_ENV: Application environment (development|staging|production)
# - DB_HOST: Database hostname
# - API_KEY: External API key
#
# Optional:
# - PORT: Server port (default: 8080)
# - LOG_LEVEL: Logging level (default: info)Check for required variables at the start:
# Will fail fast if required vars are missing
required:
environment: (( grab $APP_ENV ))
database_url: (( grab $DATABASE_URL ))
api_key: (( grab $API_KEY ))
# Then use them with confidence
config:
env: (( grab required.environment ))Establish naming conventions:
APP_prefix for application settingsDB_prefix for database settingsAWS_prefix for AWS-related settings
Use Vault for sensitive data:
# Bad - password in environment variable
password: (( grab $DB_PASSWORD ))
# Good - password from Vault
password: (( vault "secret/db:password" ))
# OK - Vault path from environment
password: (( vault (concat $VAULT_PATH ":password") ))config:
port: (( grab $PORT || 8080 ))
workers: (( grab $WORKER_COUNT || 4 ))
timeout: (( grab $TIMEOUT_SECONDS || 30 ))
retries: (( grab $MAX_RETRIES || 3 ))# Test specific expansion
echo 'value: (( grab config.$MY_VAR.key ))' | MY_VAR=test graft merge -
# Debug mode shows expansion
graft merge --debug config.yml
# List all environment variables
env | sort- Variable not set: Use
||for defaults - Empty expansion: Check variable spelling and case
- Path not found: Verify the expanded path exists
- Special characters: Stick to alphanumeric and underscore
- Environment variables are visible in process listings
- Don't store secrets directly in environment variables
- Be cautious with user-supplied environment variables
- Validate and sanitize when possible
- Use Vault or other secret management for sensitive data
# config.yml
app:
name: (( grab $APP_NAME || "my-app" ))
version: (( grab $APP_VERSION || "1.0.0" ))
environment: (( grab $APP_ENV || "development" ))
# Environment-specific settings
environments:
development:
debug: true
database:
host: localhost
port: 5432
cache:
enabled: false
production:
debug: false
database:
host: (( grab $DB_HOST ))
port: (( grab $DB_PORT || 5432 ))
cache:
enabled: true
ttl: (( grab $CACHE_TTL || 3600 ))
# Select current environment
current: (( grab environments.$APP_ENV ))
# Build connection strings
database:
url: (( concat
"postgresql://"
(grab $DB_USER || "postgres") ":"
(grab $DB_PASS || "password") "@"
(grab current.database.host) ":"
(grab current.database.port) "/"
(grab $DB_NAME || app.name)
))
# Feature flags from environment
features:
new_ui: (( grab $FEATURE_NEW_UI || current.debug ))
analytics: (( grab $FEATURE_ANALYTICS || false ))
beta: (( grab $FEATURE_BETA || current.environment == "development" ))- Operators Reference - grab and other operators
- Vault Integration - For sensitive data
- Examples - More usage examples