Frequently Asked Questions¶
General¶
What's the difference between validate and diff?¶
validatechecks a single.envfile against a Pydantic schema (type checking, required fields, extras)diffcompares two.envfiles to show differences (added, removed, changed variables)
Use validate to catch schema violations. Use diff to compare environments.
Can I use envdrift without encryption?¶
Yes. The core validate and diff commands work without encryption:
Encryption is optional but recommended for production secrets.
Can I use envdrift without a schema?¶
Partially. You can use diff without a schema:
But validate requires a schema—that's the whole point!
If you don't have a schema yet, generate one:
Does envdrift modify my .env files?¶
Only when you explicitly ask it to:
encrypt— Encrypts values in-placedecrypt— Decrypts values in-placepull— Decrypts after syncing keys
Commands like validate, diff, and encrypt --check are read-only.
Encryption¶
Should I use dotenvx or SOPS?¶
| Choose dotenvx if... | Choose SOPS if... |
|---|---|
| You want simplicity | You have existing KMS infrastructure |
| You're a small team | You need native cloud KMS integration |
| You want partial encryption | You need key rotation and audit logs |
| You're new to secrets management | You're in an enterprise environment |
See Encryption Backends for a detailed comparison.
How do I share encryption keys with my team?¶
Use vault sync:
- Push your keys to a cloud vault:
envdrift vault-push - Team members pull keys:
envdrift syncorenvdrift pull
This way, keys are stored securely in the cloud and synced on demand.
Can I encrypt only some variables?¶
With dotenvx, you can use partial encryption:
# envdrift.toml
[partial_encryption]
enabled = true
[[partial_encryption.environments]]
name = "production"
clear_file = ".env.production.clear"
secret_file = ".env.production.secret"
combined_file = ".env.production"
See Partial Encryption.
What happens if I lose my encryption keys?¶
You cannot decrypt your files. This is by design—encryption is only secure if keys are required.
Best practices:
- Store keys in a cloud vault (Azure, AWS, HashiCorp, GCP)
- Keep a secure backup of keys
- Use vault sync to ensure all team members have access
Can I rotate encryption keys?¶
dotenvx: Decrypt with old key, re-encrypt with new key, push new key to vault.
SOPS: Use your KMS provider's key rotation feature, or add new keys as recipients.
Schema¶
How do I mark a field as sensitive?¶
Use json_schema_extra:
from pydantic import Field
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
API_KEY: str = Field(json_schema_extra={"sensitive": True})
Sensitive fields trigger encryption warnings if not encrypted.
How do I make a field optional?¶
Provide a default value:
class Settings(BaseSettings):
# Required (no default)
DATABASE_URL: str
# Optional (has default)
DEBUG: bool = False
LOG_LEVEL: str = "INFO"
How do I handle nested settings?¶
Use Pydantic's nested model support:
class DatabaseSettings(BaseSettings):
URL: str
POOL_SIZE: int = 5
class Settings(BaseSettings):
DATABASE: DatabaseSettings
With env prefix:
Can I use different schemas for different environments?¶
Yes, create environment-specific classes:
from pydantic_settings import BaseSettings
class AppSettings(BaseSettings):
DATABASE_URL: str
API_KEY: str
class DevelopmentSettings(AppSettings):
DEBUG: bool = True
class ProductionSettings(AppSettings):
DEBUG: bool = False
SENTRY_DSN: str # Required only in production
envdrift validate .env.dev --schema config:DevelopmentSettings
envdrift validate .env.prod --schema config:ProductionSettings
Vault¶
Which vault provider should I use?¶
Use whatever your team already uses:
- Azure shop? → Azure Key Vault
- AWS shop? → AWS Secrets Manager
- GCP shop? → GCP Secret Manager
- Multi-cloud or self-hosted? → HashiCorp Vault
See Vault Providers for details.
Can I use multiple vault providers?¶
You can specify different providers per mapping, but you'll need to provide credentials for each when syncing.
How do I set up vault access in CI/CD?¶
Use your cloud provider's OIDC integration:
# GitHub Actions - AWS
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/my-role
aws-region: us-east-1
- run: envdrift sync --provider aws
See CI/CD Integration.
Monorepo¶
How do I use envdrift in a monorepo?¶
Use --service-dir for validation and configure multiple mappings:
# Validate service-specific schema
envdrift validate services/api/.env --schema config:Settings --service-dir services/api
# envdrift.toml
[[vault.sync.mappings]]
secret_name = "api-key"
folder_path = "services/api"
[[vault.sync.mappings]]
secret_name = "web-key"
folder_path = "services/web"
See Monorepo Setup.
Can I share a schema across services?¶
Yes, put the shared schema in a common package and import it:
# services/api/config.py
from shared.settings import BaseAppSettings
class Settings(BaseAppSettings):
API_SPECIFIC_VAR: str
CI/CD¶
How do I fail the build on validation errors?¶
Use the --ci flag:
Without --ci, validation errors are reported but don't cause a non-zero exit.
Can I validate multiple environments in CI?¶
Yes, run multiple validation commands:
- run: |
envdrift validate .env.staging --schema config:Settings --ci
envdrift validate .env.production --schema config:Settings --ci
How do I decrypt in CI without storing keys in the repo?¶
Option 1: Store private key as CI secret
- env:
DOTENV_PRIVATE_KEY: ${{ secrets.DOTENV_PRIVATE_KEY }}
run: |
echo "DOTENV_PRIVATE_KEY=$DOTENV_PRIVATE_KEY" > .env.keys
envdrift decrypt .env.production
Option 2: Use vault sync with CI credentials
Migration¶
Coming from python-dotenv?¶
envdrift reads the same .env format. Add a schema:
# Generate schema from existing .env
envdrift init .env --output config.py
# Start validating
envdrift validate .env --schema config:Settings
Coming from django-environ?¶
Map your environ.Env() calls to Pydantic:
# Before (django-environ)
import environ
env = environ.Env()
DATABASE_URL = env.db()
DEBUG = env.bool("DEBUG", default=False)
# After (pydantic-settings)
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
DATABASE_URL: str
DEBUG: bool = False
See Migrating.