Your Ansible repository describes your servers, so it needs database passwords, API keys and TLS keys. Committing them in plain text is a classic leak. Ansible Vault encrypts files or individual values with AES-256, so the repository can be shared and pushed safely, while Ansible decrypts them at runtime.
Basics: Ansible playbooks and Ansible roles.
Encrypt a whole file
ansible-vault create group_vars/prod/vault.yml # opens $EDITOR, saves encrypted
ansible-vault edit group_vars/prod/vault.yml
ansible-vault view group_vars/prod/vault.yml
ansible-vault encrypt existing-secrets.yml
ansible-vault decrypt existing-secrets.yml # back to plain text (careful)
ansible-vault rekey group_vars/prod/vault.yml # change the vault password
An encrypted file starts with $ANSIBLE_VAULT;1.1;AES256, followed by hex data. It’s safe to commit.
The vault/vars pattern (recommended)
Keep variable names visible, and only the values encrypted:
# group_vars/prod/vars.yml (plain text, readable in code review)
db_password: "{{ vault_db_password }}"
smtp_api_key: "{{ vault_smtp_api_key }}"
# group_vars/prod/vault.yml (encrypted with ansible-vault)
vault_db_password: "S3cure-Pa55"
vault_smtp_api_key: "key-abc123"
Anyone reading the playbooks can see which secrets exist, and grep finds them, without seeing the values.
Encrypt a single value
ansible-vault encrypt_string 'S3cure-Pa55' --name 'db_password'
Paste the output into any vars file:
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
6233…
Handy for a few values. Whole-file encryption is easier to manage when there are many.
Run playbooks with vault data
ansible-playbook site.yml --ask-vault-pass
ansible-playbook site.yml --vault-password-file ~/.ansible/vault-pass.txt
Or set it once in ansible.cfg:
[defaults]
vault_password_file = ~/.ansible/vault-pass.txt
Protect that file (chmod 600), and never commit it. Add it to .gitignore. Instead of a file, the password can come from a script that reads a password manager (the file is executable, and prints the password).
Multiple vaults: vault IDs
Use different passwords for different environments:
ansible-vault create --vault-id prod@prompt group_vars/prod/vault.yml
ansible-vault create --vault-id dev@~/.ansible/dev-pass group_vars/dev/vault.yml
ansible-playbook site.yml --vault-id dev@~/.ansible/dev-pass --vault-id prod@prompt
Developers can get the dev password, without access to production secrets.
Avoid leaking secrets in output
Ansible prints task parameters in verbose mode and in error messages. Mark sensitive tasks:
- name: Create database user
community.mysql.mysql_user:
name: app
password: "{{ db_password }}"
no_log: true
In CI/CD
Store the vault password as a CI secret, write it to a temporary file during the job, and pass --vault-password-file. Delete the file afterwards. See GitHub Actions secrets.
Rotation
ansible-vault rekeychanges the vault password (do it when someone leaves the team).- Changing the secrets themselves means editing the vault file, and running the playbook to apply them.
- If a plain-text secret was ever committed, rotate it. Removing it from Git history isn’t enough.
Alternatives
SOPS (with age keys), or external secret managers (HashiCorp Vault/OpenBao, Bitwarden Secrets Manager) with Ansible lookup plugins, work well for larger teams. See secrets management.
Quick reference
| Task | Command |
|---|---|
| New encrypted file | ansible-vault create file.yml |
| Edit / view | ansible-vault edit / view |
| Encrypt one value | ansible-vault encrypt_string 'x' --name var |
| Run | --ask-vault-pass or --vault-password-file |
| Change password | ansible-vault rekey file.yml |
| Hide output | no_log: true |
Related: Ansible server hardening and Linux configuration management.