Profiles
Profiles let you maintain separate configurations for different environments — development, staging, production, or any custom setup. Each profile has its own .env.<name> file that overrides the base .env.
How profiles work
envref uses a three-layer merge strategy:
.env <- .env.<profile> <- .env.local
.env— Base configuration, committed to git. Contains shared defaults andref://secret references..env.<profile>— Profile-specific overrides (e.g.,.env.staging). Committed to git..env.local— Personal overrides. Gitignored, never committed.
Each layer overrides keys from the previous layer. The last value wins.
Example
# .env (base)
APP_NAME=my-app
APP_PORT=3000
LOG_LEVEL=info
DATABASE_URL=ref://secrets/database_url
# .env.staging
APP_PORT=8080
LOG_LEVEL=debug
DATABASE_URL=ref://secrets/database_url
# .env.local (personal)
APP_PORT=9999
With the staging profile active, envref resolve produces:
APP_NAME=my-app <- from .env (no override)
APP_PORT=9999 <- from .env.local (overrides staging)
LOG_LEVEL=debug <- from .env.staging (overrides base)
DATABASE_URL=<resolved> <- ref:// resolved from backend
Managing profiles
Create a profile
envref profile create staging
This creates .env.staging with a starter template. You can also:
# Copy from an existing file as a starting point
envref profile create staging --from .env
# Register the profile in .envref.yaml
envref profile create staging --register
# Overwrite an existing profile file
envref profile create staging --force
# Use a custom file path
envref profile create staging --env-file configs/staging.env
Profile names cannot contain dots or slashes, and local is reserved (used by .env.local).
List available profiles
envref profile list
Output shows all profiles with their status:
* development .env.development (config, file)
staging .env.staging (config, file)
production .env.production (no file)
*marks the active profileconfigmeans the profile is registered in.envref.yamlfilemeans the.env.<name>file exists on diskno filemeans the profile is configured but the file hasn't been created
Set the active profile
envref profile use staging
This updates active_profile in .envref.yaml. The active profile is used by default in envref resolve, envref status, and other commands.
To deactivate the current profile:
envref profile use --clear
Compare profiles
envref profile diff staging production
Output shows differences between two profiles:
+ API_URL=https://api.prod.example.com (only in production)
- DEBUG=true (only in staging)
~ LOG_LEVEL: debug -> warn (changed)
Markers:
+— key only in the second profile-— key only in the first profile~— key in both but with different values
Supports --format json and --format table for alternative output.
Profiles in configuration
Register profiles in .envref.yaml:
project: my-app
profiles:
development:
env_file: .env.development
staging:
env_file: .env.staging
production:
env_file: .env.production
active_profile: development
You can also use convention-based discovery — envref detects .env.<name> files on disk even if they're not registered in config.
Profile-scoped secrets
Secrets can be scoped to a specific profile so that different environments use different secret values for the same key.
Storing profile-scoped secrets
# Store a staging-specific database password
envref secret set db_password --profile staging
# Store a production-specific database password
envref secret set db_password --profile production
These are stored under separate namespaces in the backend:
my-app/staging/db_password
my-app/production/db_password
How profile-scoped lookup works
When resolving ref://secrets/db_password with --profile staging:
- First, envref looks up
my-app/staging/db_password(profile-scoped) - If not found, falls back to
my-app/db_password(project-scoped)
This lets you have a default secret at the project level and override it per profile only when needed.
Managing profile-scoped secrets
All secret commands support the --profile flag:
envref secret set api_key --profile staging
envref secret get api_key --profile staging
envref secret delete api_key --profile staging
envref secret list --profile staging
envref secret generate api_key --profile staging
Using profiles with resolve
# Use the active profile (set via envref profile use)
envref resolve
# Override with a specific profile
envref resolve --profile staging
# Combine with direnv output
envref resolve --profile staging --direnv
# Strict mode — fail if any reference can't resolve
envref resolve --profile production --strict
Using profiles with other commands
# Get a value with profile merge
envref get APP_PORT --profile-file .env.staging
# Show status for a specific profile
envref status --profile staging
# Validate with a profile
envref validate --profile-file .env.staging
# Run a command with a specific profile
envref run --profile staging -- ./deploy.sh
Recommended workflow
-
Create profiles for each environment:
envref profile create development --register envref profile create staging --register envref profile create production --register -
Set defaults in
.env(shared across all environments) -
Override per profile in
.env.<name>files -
Store profile-scoped secrets where they differ:
envref secret set db_password --profile staging envref secret set db_password --profile production -
Switch profiles during development:
envref profile use staging envref resolve # uses staging profile automatically -
Use in CI/CD with explicit
--profile:envref resolve --profile production --strict
See also
- Getting Started — basic envref setup
- direnv Integration — automatic environment loading with profiles
- Secret Backends — configure where secrets are stored