Skip to content

envdrift lock

Verify keys and encrypt all env files (opposite of pull - prepares for commit).

Synopsis

envdrift lock [OPTIONS]

Description

The lock command is the opposite of pull. While pull syncs keys and decrypts files for development, lock verifies keys and encrypts files before committing.

The key-sync/verify part of this workflow is dotenvx-only. The encrypt step itself uses your configured backend, so lock can drive SOPS encryption when a [vault.sync] section is present — but the recommended SOPS path is plain envdrift encrypt. See the SOPS Backend Guide.

This command ensures your environment files are properly encrypted before committing. It can:

  1. Verify key consistency - Check that local .env.keys match vault secrets (prevents key drift)
  2. Sync keys from vault - Optionally fetch keys from vault to ensure consistency
  3. Encrypt env files - Encrypt all decrypted .env.<environment> files using the configured backend (dotenvx or SOPS)

This is the recommended command before committing changes to ensure:

  • Local encryption keys are in sync with the team's vault keys
  • All .env files are properly encrypted
  • No plaintext secrets are accidentally committed

Configuration is auto-discovered from:

  • pyproject.toml with [tool.envdrift.vault.sync] section
  • envdrift.toml with [vault.sync] section
  • Explicit --config file

Options

--config, -c

Path to sync configuration file (TOML preferred; legacy pair.txt still supported).

# Auto-discover (recommended)
envdrift lock

# Explicit config
envdrift lock -c envdrift.toml

--provider, -p

Vault provider to use. Options: azure, aws, hashicorp, gcp.

Usually read from TOML config; use this to override.

--vault-url

Vault URL. Required for Azure and HashiCorp providers.

Usually read from TOML config; use this to override.

--region

AWS region for Secrets Manager. Default: us-east-1.

--project-id

GCP project ID for Secret Manager. Required for the gcp provider unless configured in TOML.

--force, -f

Force encryption without prompting.

envdrift lock --force

--profile

Process all regular (non-profile) mappings plus the mapping tagged with this profile.

# Lock regular mappings plus the 'local' profile
envdrift lock --profile local

--verify-vault

Verify that local .env.keys match vault secrets before encrypting.

This helps detect key drift where a developer may have re-encrypted with a new key that doesn't match the team's shared vault key.

Verification is strict: a mapping that cannot be verified — missing .env.keys, a missing key entry, a missing or empty vault secret, or a vault access error — fails the same way a key mismatch does. When any mapping fails verification, lock exits 1 before encrypting anything (even with --force, which only skips prompts). This prevents the encryption step from silently minting a fresh local-only key that the rest of the team cannot decrypt with the vault key. Repair with envdrift lock --sync-keys (fetch keys from vault) or envdrift vault-push (publish local keys), or rerun without --verify-vault to encrypt without verification.

envdrift lock --verify-vault

--sync-keys

Sync keys from vault before encrypting. This implies --verify-vault.

Use this to ensure your local keys are up-to-date with vault before encrypting.

The same hard-failure contract applies: when any mapping's key fails to sync (missing vault secret, vault error), lock exits 1 before encrypting anything — even with --force. Encrypting past a failed sync would mint a fresh local-only key for the very mapping whose vault key is unavailable. The verification phase checks that every configured vault secret exists and contains usable key material even when its mapping has no local env file yet; an unavailable or unusable secret is an error, not a successful skip.

envdrift lock --sync-keys

--check

Check encryption status only (dry run). Reports what would be encrypted without making changes.

A file counts as encrypted only when no plaintext value remains — the same predicate envdrift push uses, applied to every backend (dotenvx and SOPS alike). A mixed file holding even one freshly added plaintext secret fails the check, no matter how many other values are already ciphertext. The plaintext DOTENV_PUBLIC_KEY_* header line and SOPS's sops_* metadata trailer are ignored, so fully-encrypted files pass regardless of how few variables they hold.

lock --check is deliberately stricter than envdrift encrypt --check: the latter flags only values that look like secrets (sensitive names, suspicious values, or schema-marked fields), while lock --check fails on any plaintext value left in a file that is supposed to be fully encrypted.

Exits 1 when any file still needs encryption (including, with --all, partial-encryption .secret files); exits 0 only when everything is encrypted.

envdrift lock --check

--all

Include partial encryption files in the locking process. When set:

  1. No longer skips combined files - Partial encryption combined files (e.g., .env.production) get encrypted like regular files
  2. Encrypts .secret files - Ensures partial encryption secret files are also locked
  3. Deletes combined files - Removes the merged combined files after locking

This is useful when you want to lock everything and clean up generated files before committing.

Secrets-only environments are skipped — but still checked. Combine-mode handling does not apply to environments configured with secrets_only = true — they have no combined file and are encrypted in place with envdrift push. lock --all reports them as skipped and leaves them untouched. If a skipped environment still holds plaintext secrets, the final summary says so and lock --all exits 1 instead of printing the green "ready to commit" banner — run envdrift push to encrypt them.

# Lock everything including partial encryption files
envdrift lock --all

# Force mode with partial encryption
envdrift lock -f --all

# Check what would happen
envdrift lock --check --all

Smart Encryption

When smart_encryption is enabled in your configuration, the lock command will skip re-encryption if the file content hasn't changed. This reduces unnecessary git noise caused by non-deterministic encryption algorithms used by dotenvx and SOPS.

The Problem

Both dotenvx (ECIES) and SOPS use encryption that produces different ciphertext each time, even for identical plaintext. This means:

  • Decrypting and re-encrypting an unchanged file creates a "modified" file in git
  • Git history becomes cluttered with meaningless changes
  • Code reviews show confusing diffs

The Solution

With smart encryption enabled:

  1. Before re-encrypting, the command compares current content with the decrypted git version
  2. If content is unchanged, it restores the original encrypted file from git
  3. No new ciphertext is generated, so git shows no changes

Configuration

Enable in your envdrift.toml:

[encryption]
backend = "dotenvx"
smart_encryption = true

Example Output

Step 1: Encrypting environment files...

  = .env.production - skipped (content unchanged, restored encrypted version from git)
  + .env.staging - encrypted

Examples

Basic Lock

# Encrypt all env files
envdrift lock

Verify Keys Then Lock

# Check keys match vault, then encrypt
envdrift lock --verify-vault

Sync Keys Then Lock

# Fetch keys from vault, then encrypt
envdrift lock --sync-keys

Check-Only Mode (Dry Run)

# See what would be encrypted
envdrift lock --check

Lock With Profile

# Lock a specific profile
envdrift lock --profile local

Lock Including Partial Encryption Files

# Lock everything, including partial encryption files
envdrift lock -f --all

Force Lock Without Prompts

# CI-friendly: encrypt all without prompting
envdrift lock --force

Override Provider Settings

envdrift lock -p azure --vault-url https://myvault.vault.azure.net/

Output

Without Vault Verification

Lock - Verifying keys and encrypting env files
Provider: azure | Mode: Interactive | Services: 3

Step 1: Encrypting environment files...

  + services/myapp/.env.production - encrypted
  = services/auth/.env.production - skipped (already encrypted)

╭──────────── Lock Summary ────────────╮
│ Encrypted: 1                         │
│ Already encrypted: 1                 │
│ Skipped: 0                           │
│ Errors: 0                            │
╰──────────────────────────────────────╯

Lock complete! Your environment files are encrypted and ready to commit.

With Vault Verification

Lock - Verifying keys and encrypting env files
Provider: azure | Mode: Interactive | Services: 2

Step 1: Verifying keys with vault...

  ✓ services/myapp - keys match vault
  ✗ services/auth - KEY MISMATCH: local key differs from vault!

[ERROR] Found 1 failed key verification(s). Nothing was encrypted.
Run 'envdrift lock --sync-keys' to sync keys from vault, or rerun without
--verify-vault to skip verification.

An unverifiable mapping fails the same way (exit 1, nothing encrypted):

Step 1: Verifying keys with vault...

  ✗ services/auth - cannot verify: .env.keys not found

[ERROR] Found 1 failed key verification(s). Nothing was encrypted.
Run 'envdrift lock --sync-keys' to sync keys from vault, or rerun without
--verify-vault to skip verification.

A vault secret that cannot be parsed as key material at all (a JSON document without a usable key field, a multi-line blob with no key line, a binary payload) is reported as KEY UNUSABLE with the shape problem, and the summary counts it as an unusable vault key rather than a failed key verification — fix the secret in the vault; --sync-keys cannot install it.

With Key Sync

Lock - Verifying keys and encrypting env files (profile: local)
Provider: azure | Mode: FORCE | Services: 1

Step 1: Verifying keys with vault...

Processing: . (local-key, env: local)
  + . (local-key, env: local) - created

╭──────────── Sync Summary ────────────╮
│ Services processed: 1                │
│ Created: 1                           │
│ Updated: 0                           │
│ Skipped: 0                           │
│ Errors: 0                            │
╰──────────────────────────────────────╯

Step 2: Encrypting environment files...

  + ./.env.local - encrypted

╭──────────── Lock Summary ────────────╮
│ Encrypted: 1                         │
│ Already encrypted: 0                 │
│ Skipped: 0                           │
│ Errors: 0                            │
╰──────────────────────────────────────╯

Lock complete! Your environment files are encrypted and ready to commit.

With --all (Partial Encryption)

Lock - Verifying keys and encrypting env files
Provider: azure | Mode: FORCE | Services: 3 | Including partial encryption

Step 1: Encrypting environment files...

  + services/api/.env.production - encrypted
  = services/auth/.env.production - skipped (already encrypted)
  + synapse/.env.production - encrypted

Step 2: Processing partial encryption files...

  = synapse/.env.production.secret - skipped (already encrypted)
  - synapse/.env.production - deleted (combined file)

╭──────────── Lock Summary ────────────╮
│ Encrypted: 2                         │
│ Already encrypted: 2                 │
│ Skipped: 0                           │
│ Errors: 0                            │
│ Partial secrets encrypted: 0         │
│ Combined files deleted: 1            │
╰──────────────────────────────────────╯

Lock complete! Your environment files are encrypted and ready to commit.

Warnings and Errors

The lock command catches many edge cases and provides helpful warnings and errors:

Warnings

Warning Meaning
.env.keys not found No encryption keys file exists; a new key will be generated
{KEY} missing from .env.keys The expected private key for this environment isn't in the keys file
vault secret is empty The vault secret exists but has no value
vault secret not found The secret doesn't exist in the vault
multiple .env files found Multiple .env.* files exist; specify the environment explicitly
file not found The expected .env.<environment> file doesn't exist
partially encrypted (plaintext values remain) The file mixes ciphertext with plaintext values; will re-encrypt
partial encryption combined file, use --all to include File is a generated combined file; use --all to include

Errors

Error Meaning
KEY MISMATCH Local key differs from vault key - potential key drift
KEY UNUSABLE The vault secret's key material is malformed or unusable - fix the vault secret shape
vault error Failed to access or authenticate with the vault
encryption failed The configured backend (dotenvx or SOPS) failed to encrypt the file
Key sync failed Could not sync keys from vault

Exit Codes

Code Meaning
0 Success (all files encrypted or verified)
1 Error (key mismatch, encryption failure, or vault error)
1 A mapping's folder_path does not exist (broken sync config, reported per row)
1 A mapped env file or .env.keys cannot be read (e.g. not valid UTF-8) — per-file error
1 --verify-vault: a mapping failed or could not be verified (nothing is encrypted)
1 --check: one or more files still need encryption
1 --all: a skipped secrets-only environment still holds plaintext (run envdrift push)

Configuration File Format

Same as envdrift pull and envdrift sync. See sync documentation for details.

Example envdrift.toml:

[vault]
provider = "azure"

[vault.azure]
vault_url = "https://my-keyvault.vault.azure.net/"

[vault.sync]
default_vault_name = "my-keyvault"

# Regular mapping (always processed)
[[vault.sync.mappings]]
secret_name = "myapp-key"
folder_path = "services/myapp"
environment = "production"

[[vault.sync.mappings]]
secret_name = "postgres-key"
folder_path = "secrets/postgresql"
environment = "production"  # Key stays DOTENV_PRIVATE_KEY_PRODUCTION
env_file = "postgresql.env" # Encrypt secrets/postgresql/postgresql.env

# Profile mappings (processed only with --profile)
[[vault.sync.mappings]]
secret_name = "local-key"
folder_path = "."
profile = "local"
activate_to = ".env"

Workflow: pull and lock

The pull and lock commands form a complete development workflow:

# Starting development (decrypt files)
envdrift pull

# ... make changes to .env files ...

# Before committing (encrypt files)
envdrift lock --verify-vault

# If keys are out of sync
envdrift lock --sync-keys

CI/CD Pre-commit Hook

# pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: envdrift-lock
        name: Ensure env files are encrypted
        entry: envdrift lock --check --force
        language: system
        files: ^\.env\.(production|staging|development)$
        pass_filenames: false

Prerequisites

  • Cloud vault credentials configured (Azure CLI, AWS credentials, etc.)
  • The configured encryption backend installed (dotenvx or SOPS)

See Also

  • pull - Pull keys and decrypt files (opposite of lock)
  • sync - Sync keys only (without encryption/decryption)
  • encrypt - Encrypt a single .env file
  • decrypt - Decrypt a single .env file
  • vault-push - Push keys to vault
  • vault-pull - Config-free single-secret pull + decrypt