If you find this add-on useful, please star it on GitHub — stars show appreciation and help maintainers know their work matters.
A DDEV addon that provides a centralized, encrypted local vault for project secrets. Secrets are stored outside your project directory (at ~/.ddev/secret-vault/) so they can never be accidentally committed to git, and are automatically injected into .ddev/.env when DDEV starts.
openssl (standard on macOS and most Linux distros)python3 (standard on macOS and most Linux distros)ddev add-on get RandalVanheede/secretvault-ddev
# 1. Initialize a vault for your project
ddev vault init
# 2. Import existing secrets automatically
ddev vault import
# 3. Or add secrets manually
ddev vault set STRIPE_SECRET_KEY sk_test_abc123
# 4. Start DDEV — secrets are injected automatically
ddev start
| Command | Description |
|---|---|
ddev vault init |
Initialize an encrypted vault for this project |
ddev vault set KEY VALUE |
Store or update a secret |
ddev vault get KEY |
Print a secret’s value |
ddev vault list |
List all secret keys |
ddev vault list --values |
List keys with masked values |
ddev vault remove KEY |
Delete a secret |
ddev vault inject |
Write secrets to .ddev/.env (also runs on ddev start) |
| Command | Description |
|---|---|
ddev vault import |
Auto-detect .env and settings.local.php and import |
ddev vault import --from=.env |
Import from a specific file |
ddev vault import --from=web/sites/default/settings.local.php |
Import from a PHP file |
ddev vault import --clean |
Import AND sanitize source files (backup created first) |
| Command | Description |
|---|---|
ddev vault export |
Print all secrets as .env format |
ddev vault export secrets.env |
Export to a file |
ddev vault status |
Show vault info and injection status |
ddev vault change-password |
Change the master password |
Vaults live at ~/.ddev/secret-vault/<project-name>.vault — outside your project, unreachable by git.
~/.ddev/secret-vault/
├── my-drupal-site.vault # AES-256-CBC encrypted
└── other-project.vault
Secrets are encrypted with openssl aes-256-cbc -pbkdf2. On macOS, the master password is offered to be saved in the macOS Keychain so you aren’t prompted on every ddev start. On Linux, secret-tool (libsecret) is used if available.
A pre-start hook runs ddev vault inject --quiet before containers start. Secrets are written to a clearly-marked block in .ddev/.env:
# --- secret-vault: BEGIN ---
DB_PASSWORD="super_secret"
DRUPAL_HASH_SALT="abc123..."
STRIPE_SECRET_KEY="sk_test_..."
# --- secret-vault: END ---
The block is replaced on every inject — no stale secrets accumulate.
.gitignore Safetyddev vault init automatically adds .ddev/.env and .ddev/.env.* to your project’s .gitignore.
.envddev vault import --from=.env
Parses KEY=VALUE lines, strips quotes, skips comments.
settings.local.phpddev vault import --from=web/sites/default/settings.local.php
Extracts:
$settings['hash_salt'] → DRUPAL_HASH_SALT$databases[...]['password'] → DB_PASSWORD$settings['smtp_password'] → SMTP_PASSWORD$settings key matching common sensitive patterns (api_key, auth_token, etc.)When DDEV is running, the PHP-based extractor (extract-secrets.php) runs inside the container for more accurate extraction. Otherwise, a pure-Python regex extractor is used as fallback.
--clean: Sanitize Source Filesddev vault import --from=web/sites/default/settings.local.php --clean
After importing, shows a diff and (with confirmation) replaces hardcoded values:
-$settings['hash_salt'] = 'abc123secrethash';
+$settings['hash_salt'] = getenv('DRUPAL_HASH_SALT');
- 'password' => 'super_secret_password',
+ 'password' => getenv('DB_PASSWORD'),
A .bak backup is always created before any modifications.
{
"version": 1,
"project": "my-drupal-site",
"created": "2026-05-20T13:30:00Z",
"updated": "2026-05-20T14:00:00Z",
"secrets": {
"DRUPAL_HASH_SALT": "abc123...",
"SMTP_PASSWORD": "hunter2"
},
"metadata": {
"DRUPAL_HASH_SALT": {
"imported_from": "settings.local.php",
"imported_at": "2026-05-20T13:30:00Z",
"updated_at": "2026-05-20T13:30:00Z"
}
}
}
ddev-secret-vault/
├── install.yaml # DDEV addon manifest
├── config.secret-vault.yaml # pre-start hook
├── commands/host/vault # Main host command
├── secret-vault-helpers/
│ ├── crypto.sh # Encryption / keychain helpers
│ ├── ui.sh # Colors, prompts, diff output
│ ├── import-env.sh # .env import logic
│ ├── import-settings-local.sh # settings.local.php import logic
│ └── extract-secrets.php # PHP extractor (runs in container)
└── tests/
├── test-import-env.sh
└── test-import-settings.sh
bash tests/test-import-env.sh
bash tests/test-import-settings.sh
Tests run entirely without DDEV — they create temporary vaults, import from fixture files, and assert on extracted values.
AES-256-CBC with PBKDF2 (100,000 iterations) — adequate for local development secretschmod 600; the vault directory is chmod 700If you find this add-on useful, please star it on GitHub — stars show appreciation and help maintainers know their work matters.