Here is how to use Ansible Vault in four steps: encrypt the secret with ansible-vault create or ansible-vault encrypt_string, commit the encrypted file or !vault block next to your playbooks, reference it like any other variable, and run ansible-playbook with --ask-vault-pass, --vault-password-file or --vault-id so Ansible can decrypt it at run time.
That is the whole loop. Everything else is choosing between file and string encryption, deciding how many passwords you want to juggle, and not leaking the secret after Ansible has decrypted it. That last part is the one that actually bites. The official overview is blunt that Vault only protects data at rest: once a play is running, the decrypted value is just another variable, and keeping it out of logs is your job.
Who this is for: anyone with a Git repository of playbooks that currently has a database password sitting in plaintext in group_vars. If you already run a password manager or a cloud secrets manager, keep it. Ansible Vault works fine alongside one, and the tidiest setup feeds the vault password from that manager with a client script, covered in step 4.
Should you encrypt whole files or single strings?
Encrypt whole files by default, and use single encrypted strings only when a secret has to sit inside an otherwise readable file. The encryption guide lays out the trade: an encrypted file hides everything, including variable names, but is easy to rekey. An encrypted variable keeps the file legible, but the docs state plainly that you cannot rekey encrypted variables.
That rekey limitation matters more than it looks: the day someone with the password leaves, inline strings mean re-encrypting every value by hand. The pattern in step 3 gets you most of the legibility of inline strings without giving up easy rotation.
Vault also covers more than group_vars: the documented list includes files passed with -e @file.yml, include_vars and vars_files targets, role defaults and vars, tasks and handlers files, and arbitrary binary files such as a TLS private key.
Step 1: create or encrypt a vault file
Five subcommands cover day-to-day file work:
# New file: opens $EDITOR, encrypts on save
ansible-vault create group_vars/prod/vault.yml
# Existing plaintext file: encrypts in place
ansible-vault encrypt group_vars/prod/vault.yml
# Read it without changing anything
ansible-vault view group_vars/prod/vault.yml
# Change it: decrypts to a temp file, re-encrypts when you close the editor
ansible-vault edit group_vars/prod/vault.yml
# Remove encryption permanently (rarely what you want)
ansible-vault decrypt group_vars/prod/vault.yml
Each command prompts for the vault password unless you give it a source. The result starts with a header line, $ANSIBLE_VAULT;1.1;AES256, or $ANSIBLE_VAULT;1.2;AES256;prod when a vault ID label was used, followed by hex-encoded ciphertext. The format documentation specifies AES256 with a key derived by PBKDF2 using SHA256 over 10,000 iterations, plus an HMAC so tampering is detected. The encrypted file is safe to commit. The password is not.
edit is the subcommand to be careful with. It writes a decrypted temporary copy, hands it to whatever $EDITOR points at, and deletes it when you close. The docs recommend configuring your editor so it leaves no swap files, backup files, viminfo history or clipboard copies of the plaintext behind. If you live in Vim, that is ten minutes of config worth doing once, before the first secret rather than after.
Step 2: encrypt a single value with encrypt_string
When a secret has to live inline in a readable file, encrypt_string produces a YAML block you paste in:
ansible-vault encrypt_string --vault-id prod@prompt --stdin-name 'db_password'
Type the value, then press Ctrl-D. Do not press Enter first; the docs warn that adds a newline to the encrypted value, and you will spend an evening wondering why the database rejects a password you can see is correct. The output looks like this:
db_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;prod
62313365396662343061393464336163383764373764613633653634306231386433626436623361
...
The !vault tag tells Ansible to decrypt the value when it is used. The docs also show piping the value in, echo -n 'letmein' | ansible-vault encrypt_string ... --stdin-name 'db_password', and passing it as a positional argument. Both work, and both leave the secret in your shell history, which somewhat defeats the point.
Step 3: keep variable names visible with a vars and vault split
Ansible’s own tips page recommends this layout. Make group_vars/<group>/ a directory holding two files. The vars file defines every variable the plays use, in plaintext. The vault file holds the secret values, each prefixed with vault_, and is the only file you encrypt. The vars file then points at the vault copies:
# group_vars/prod/vars.yml (plaintext, committed)
db_host: db01.internal
db_user: app
db_password: "{{ vault_db_password }}"
# group_vars/prod/vault.yml (encrypted with ansible-vault, committed)
vault_db_password: correct-horse-battery-staple
The payoff is that grep -r db_password finds where the variable is defined and used without anyone decrypting anything, and a reviewer reading vars.yml can see which secrets a group depends on. The cost, per the same page: every vault password referenced by the inventory must be available whenever you run a playbook against it. How Ansible loads group_vars directories, and in what order, is covered in Ansible inventory groups, variables and patterns.
Step 4: give Ansible the password at run time
Pick one of three command-line sources, or set it once in configuration:
# Prompt every run
ansible-playbook site.yml --ask-vault-pass
# Read from a file, or run an executable that prints the password
ansible-playbook site.yml --vault-password-file ~/.ansible/vault-prod.txt
# Labelled vault IDs, one or several
ansible-playbook site.yml --vault-id dev@dev-password --vault-id prod@prompt
# ansible.cfg
[defaults]
vault_password_file = ~/.ansible/vault-prod.txt
The configuration reference maps vault_password_file to the ANSIBLE_VAULT_PASSWORD_FILE environment variable. Its sibling, vault_identity_list, is equivalent to passing several --vault-id arguments. These flags sit alongside the rest in ansible-playbook options that change a run.
Two rules for the password file. Keep it outside the repository with chmod 600, and add its name to .gitignore anyway, because someone will eventually copy it into the repo “just for a minute.” Then consider replacing it with a script. Any executable password file is run and its standard output used as the password, so a small script can pull the password from your OS keyring or secrets manager instead of a plaintext file. The password management guide sets the contract: name the script with a -client or -client.EXTENSION suffix, accept a --vault-id option, print the password to stdout, and send any prompts to the TTY. Ansible then calls it as vault-keyring-client.py --vault-id dev, so one script can serve several labels.
How do vault IDs work with more than one password?
A vault ID is a label plus a password source, written label@source, where the source is a file, a client script or the word prompt. Use separate IDs when different people should hold different secrets, such as a dev password the whole team knows and a prod password only a few people have. For a small team, the docs say one password for everything is fine, and it is less to lose.
Labels are hints, not access control. By default Ansible tries the password whose label matches the encrypted data first, then every other supplied password in the order given on the command line. If you want each password used only on data carrying its own label, turn on vault_id_match. When more than one ID is loaded and you encrypt something new, choose the identity with --encrypt-vault-id prod, or set a default so nobody has to remember:
[defaults]
vault_id_match = True
vault_encrypt_identity = prod
Rotating a password with rekey
ansible-vault rekey changes the password, the vault ID, or both on one or more files. It asks for the original password and then the new one:
ansible-vault rekey group_vars/prod/vault.yml
Add --new-vault-id to move the file to a different vault ID at the same time, or --new-vault-password-file for scripted rotation. This is where whole-file encryption pays off, since inline !vault strings have to be re-created one at a time.
One thing rekey cannot do is rewrite history. The old ciphertext is still in every earlier Git commit, and the old password still opens it. If you are rotating because someone left or a laptop went missing, rotate the underlying database and API credentials too, not just the vault password.
Where does Ansible Vault stop protecting you?
Vault stops protecting a secret the moment Ansible decrypts it, because encryption only covers data at rest. After that, anything that prints variables or module arguments can expose it. The usual leaks are --diff on templates, high verbosity, and vaulted files copied to hosts, which arrive decrypted by design.
The specific ones to watch:
- Diff output.
--check --diffon a template that renders a password prints the password. Setdiff: falseon that task, orno_log: trueto suppress the whole result. - Verbosity.
-vvvshows module arguments. Treat verbose logs from CI as sensitive. - Files on target hosts. A vaulted file used as
srcforcopy,template,unarchive,scriptorassembleis decrypted on the target, per the usage guide. That is usually what you want for a TLS key, so setmode: "0600"and an owner on the task. - Editor droppings. Swap and backup files from
ansible-vault edit, as covered in step 1.
Vault is one good layer for a Git-backed repository, not a whole security strategy. For how credentials leak in the wider world, TechSentinel covers the broader security news.
FAQ
where should i store the ansible vault password file
Store it outside the repository, readable only by you with chmod 600, and point Ansible at it through vault_password_file in ansible.cfg or the ANSIBLE_VAULT_PASSWORD_FILE variable. Better still, replace the file with an executable client script that fetches the password from your OS keyring or a secrets manager at run time.
how do i view an ansible vault file without decrypting it
Run ansible-vault view path/to/file.yml and enter the password. It prints the decrypted contents to your terminal while the file on disk stays encrypted. Use ansible-vault edit to change contents in place. Avoid ansible-vault decrypt for quick checks, because it removes encryption permanently and it is easy to commit the result.
is ansible vault secure enough to commit secrets to git
Yes, for data at rest. Vault uses AES256 with a PBKDF2-derived key and an HMAC, so encrypted files are safe in a repository as long as the password is strong and stored separately. The real risks are a committed password file, secrets printed by diff or verbose output, and old ciphertext surviving in Git history.
how do i use ansible vault in a ci pipeline
Store the vault password in your CI system’s secret store, write it to a temporary file during the job, and set ANSIBLE_VAULT_PASSWORD_FILE to that path. Ansible then decrypts without prompting. Keep verbosity low, avoid --diff on secret-rendering tasks, and use no_log: true so job logs do not capture decrypted values.
Related across the network
- Self-Host Vaultwarden (Bitwarden) with Docker Compose — dockerhomelab.com
- Backup Strategies for a Self-Hosted Stack (3-2-1) — selfhostrealm.com