Ansible Vault ID — Multi-Password Vault Management
Introduction
Ansible Vault encrypts sensitive data — passwords, keys, tokens. But real projects need multiple vault passwords: one for dev, one for staging, one for production. Vault IDs solve this by labeling encrypted content with an identifier, so Ansible knows which password to use for each secret.
Basic Vault ID Usage
# Encrypt with a vault ID label
ansible-vault encrypt --vault-id dev@prompt vars/dev-secrets.yml
ansible-vault encrypt --vault-id prod@prompt vars/prod-secrets.yml
# Decrypt/edit with vault ID
ansible-vault edit --vault-id dev@prompt vars/dev-secrets.yml
# Run playbook with multiple vault IDs
ansible-playbook site.yml \
--vault-id dev@prompt \
--vault-id prod@prompt
Password Sources
Prompt (Interactive)
ansible-vault encrypt --vault-id myapp@prompt secrets.yml
# Prompts: "Vault password (myapp):"
Password File
# Create password files
echo "dev-password-here" > ~/.vault/dev.pass
echo "prod-password-here" > ~/.vault/prod.pass
chmod 600 ~/.vault/*.pass
# Use password files
ansible-vault encrypt --vault-id dev@~/.vault/dev.pass vars/dev.yml
ansible-vault encrypt --vault-id prod@~/.vault/prod.pass vars/prod.yml
# Run with password files
ansible-playbook site.yml \
--vault-id dev@~/.vault/dev.pass \
--vault-id prod@~/.vault/prod.pass
Client Script
#!/bin/bash
# ~/.vault/vault-client.sh — retrieves password from external source
# Receives vault-id label as argument
case "$1" in
dev)
# From environment variable
echo "${VAULT_DEV_PASSWORD}"
;;
prod)
# From secrets manager
aws secretsmanager get-secret-value \
--secret-id ansible/prod-vault \
--query SecretString --output text
;;
*)
echo "Unknown vault-id: $1" >&2
exit 1
;;
esac
chmod +x ~/.vault/vault-client.sh
# Use the client script
ansible-playbook site.yml \
--vault-id dev@~/.vault/vault-client.sh \
--vault-id prod@~/.vault/vault-client.sh
ansible.cfg Configuration
# ansible.cfg
[defaults]
# Default vault identity list (replaces --vault-id on command line)
vault_identity_list = dev@~/.vault/dev.pass, prod@~/.vault/prod.pass
# Or use a client script
# vault_identity_list = dev@~/.vault/vault-client.sh, prod@~/.vault/vault-client.sh
Per-Environment Secrets Structure
project/
├── ansible.cfg
├── playbooks/
│ └── deploy.yml
├── group_vars/
│ ├── all/
│ │ └── vars.yml # Unencrypted shared vars
│ ├── dev/
│ │ ├── vars.yml # Unencrypted dev vars
│ │ └── vault.yml # Encrypted with vault-id "dev"
│ ├── staging/
│ │ ├── vars.yml
│ │ └── vault.yml # Encrypted with vault-id "staging"
│ └── production/
│ ├── vars.yml
│ └── vault.yml # Encrypted with vault-id "prod"
└── inventories/
├── dev.yml
├── staging.yml
└── production.yml
# Encrypt each environment's secrets with its own vault ID
ansible-vault encrypt --vault-id dev@~/.vault/dev.pass group_vars/dev/vault.yml
ansible-vault encrypt --vault-id staging@~/.vault/staging.pass group_vars/staging/vault.yml
ansible-vault encrypt --vault-id prod@~/.vault/prod.pass group_vars/production/vault.yml
Inline Encrypted Variables
# Encrypt a single string with a vault ID
ansible-vault encrypt_string --vault-id prod@~/.vault/prod.pass \
'super-secret-password' --name 'db_password'
Output (paste into vars file):
db_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;prod
61626364656667...
The prod label after AES256; tells Ansible which vault ID decrypts it.
Mixing Encrypted and Unencrypted
# group_vars/production/vault.yml (encrypted)
vault_db_password: "encrypted-value"
vault_api_key: "encrypted-value"
vault_ssl_key: "encrypted-value"
# group_vars/production/vars.yml (plain text, references vault vars)
db_host: db.example.com
db_port: 5432
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"
Rekeying
# Change the password for a vault ID
ansible-vault rekey --vault-id prod@~/.vault/prod.pass \
--new-vault-id prod@~/.vault/prod-new.pass \
group_vars/production/vault.yml
# Rekey multiple files
ansible-vault rekey --vault-id prod@~/.vault/prod.pass \
--new-vault-id prod@prompt \
group_vars/production/*.yml
CI/CD Integration
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create vault password file
run: echo "${{ secrets.VAULT_PROD_PASSWORD }}" > .vault-pass
- name: Run playbook
run: |
ansible-playbook deploy.yml \
--vault-id prod@.vault-pass \
-i inventories/production.yml
- name: Clean up
if: always()
run: rm -f .vault-pass
Troubleshooting
| Issue | Solution |
|---|---|
| "Decryption failed" | Wrong vault ID or password for the file |
| "No vault secrets found" | Missing --vault-id or vault_identity_list |
| Can't decrypt inline var | Vault ID label in $ANSIBLE_VAULT header must match |
| Password file not found | Check path; use absolute path in ansible.cfg |
| Multiple prompts annoying | Use password files or vault client script |
Best Practices
- One vault ID per environment —
dev,staging,prodwith different passwords - Never commit password files — add
*.passto.gitignore - Use
vault_prefix — name encrypted varsvault_db_password, reference asdb_password - Use client scripts — pull passwords from AWS Secrets Manager, HashiCorp Vault, etc.
- Rekey regularly — change vault passwords when team members leave
- Limit prod access — only CI/CD and ops team have prod vault password
Conclusion
Vault IDs bring proper secret management to Ansible. Instead of one password for everything, each environment gets its own encryption key. Combined with client scripts that pull passwords from external secret managers, you get enterprise-grade secret management without leaving the Ansible ecosystem.