Database passwords, API tokens, TLS private keys — these can't sit in
group_vars/all.yml in plain text if that file is committed to git. Anybody with
read access to the repo would have production credentials. Ansible Vault is
the built-in solution: encrypt the file with a password, commit the encrypted bytes,
keep the password out of git.
# Create your secrets file in plain text
cat > group_vars/all/vault.yml << EOF
vault_mysql_root_password: "ChangeMeNow123!"
vault_appuser_password: "AnotherSecret456!"
vault_api_token: "tok_aBc123XyZ"
EOF
# Encrypt in place
ansible-vault encrypt group_vars/all/vault.yml
# You'll be prompted twice for a password. The file now starts with:
head -1 group_vars/all/vault.yml
# $ANSIBLE_VAULT;1.2;AES256
# Safe to commit
git add group_vars/all/vault.yml && git commit -m "Add encrypted vault"
# Open the encrypted file in $EDITOR — decrypt, edit, re-encrypt automatically
ansible-vault edit group_vars/all/vault.yml
# View without editing
ansible-vault view group_vars/all/vault.yml
# Decrypt (gives back plain text — be careful, don't commit!)
ansible-vault decrypt group_vars/all/vault.yml
# Change the vault password (re-encrypt with a new one)
ansible-vault rekey group_vars/all/vault.yml
# Inspect: which vault id encrypted this file?
head -1 group_vars/all/vault.yml
# $ANSIBLE_VAULT;1.2;AES256;production
# ^^^^^^^^^^ — the vault id
# Prompt for password interactively
ansible-playbook site.yml --ask-vault-pass
# Read password from a file (NOT committed!)
ansible-playbook site.yml --vault-password-file ~/.ansible/vault_pass
# Read from a script that prints the password (great for CI / 1Password)
ansible-playbook site.yml --vault-password-file ./scripts/get_vault_pass.sh
# Configure default in ansible.cfg so you don't repeat the flag
echo "[defaults]
vault_password_file = ~/.ansible/vault_pass" >> ansible.cfg
ansible-playbook site.yml
Sometimes you want one secret inside an otherwise plain file. encrypt_string
produces an encrypted YAML scalar you can paste anywhere:
# Generate an encrypted scalar
ansible-vault encrypt_string 'SuperSecret123!' --name 'mysql_root_password'
# Output (paste into any plain YAML file)
mysql_root_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;default
61356638316563663...
393633623761306...
3263333766613664...
---
# group_vars/databases.yml — plain settings + ONE encrypted value
mysql_port: 3306
mysql_max_connections: 200
mysql_role: primary
# Just this one value is encrypted
mysql_root_password: !vault |
$ANSIBLE_VAULT;1.2;AES256
61356638316563663831...
...
A common pattern: dev, staging, and production each have their own vault password so a junior dev with the dev password can't decrypt prod secrets. Tag each encrypted file with a vault id:
# Encrypt with a named vault id
ansible-vault encrypt --vault-id dev@~/.vault_pass_dev group_vars/all/vault_dev.yml
ansible-vault encrypt --vault-id prod@~/.vault_pass_prod group_vars/all/vault_prod.yml
# Look at the headers — each file is tagged
head -1 group_vars/all/vault_dev.yml
# $ANSIBLE_VAULT;1.2;AES256;dev
head -1 group_vars/all/vault_prod.yml
# $ANSIBLE_VAULT;1.2;AES256;prod
# Provide both vault passwords; Ansible auto-picks the right one per file
ansible-playbook site.yml \
--vault-id dev@~/.vault_pass_dev \
--vault-id prod@~/.vault_pass_prod
# Or set in ansible.cfg
[defaults]
vault_identity_list = dev@~/.vault_pass_dev, prod@~/.vault_pass_prod
| Goal | Command |
|---|---|
| Encrypt a file | ansible-vault encrypt FILE |
| Decrypt a file | ansible-vault decrypt FILE |
| Edit in place | ansible-vault edit FILE |
| Show plain content | ansible-vault view FILE |
| Change password | ansible-vault rekey FILE |
| Encrypt one string | ansible-vault encrypt_string 'val' --name 'key' |
| Run with prompt | ansible-playbook --ask-vault-pass |
| Run with file | ansible-playbook --vault-password-file FILE |
| Multi-vault run | --vault-id dev@FILE --vault-id prod@FILE |