Managing Ansible Vault Secrets Without Losing Your Mind
Ansible Vault is the simplest way to keep secrets in your repo without leaking them — if you set it up right. Here's a battle-tested workflow for teams.
- #iac
- #ansible
- #ansible-vault
- #secrets
- #security
- #ai
Every Ansible codebase hits the same wall eventually: you need a database password, an API token, or a TLS private key inside a playbook, and you absolutely cannot commit it in plaintext. Ansible Vault is the built-in answer, and it’s genuinely good — but most teams configure it once, badly, and then spend years tripping over the rough edges.
I’ve cleaned up enough Vault setups to know the patterns that hold up. Here’s the workflow I reach for now.
What Vault actually does (and doesn’t)
Ansible Vault encrypts files (or individual variables) at rest using AES-256, gated by a password. That’s the whole feature. It does not rotate secrets, it does not integrate with an external KMS by default, and it does not stop someone with the vault password from reading everything.
So the mental model is: Vault keeps secrets out of your git history in plaintext, and that’s the bar it clears. For anything needing rotation, audit trails, or dynamic credentials, you’ll eventually reach for HashiCorp Vault, AWS Secrets Manager, or SOPS. But for a small-to-midsize Ansible repo, Vault is the right amount of tool.
Encrypt variables, not whole files
The most common mistake is encrypting entire vars files. When you do that, every change shows up as an opaque blob in code review, and you can’t tell what changed without decrypting.
Use encrypt_string to encrypt individual values instead, and keep them inline in an otherwise-readable vars file:
# group_vars/production/vault.yml
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66386439653...truncated...3737
api_token: !vault |
$ANSIBLE_VAULT;1.1;AES256
39653831663...truncated...6261
Generate each one with:
ansible-vault encrypt_string 'super-secret-pw' --name 'db_password'
Now git diff shows you which variable changed even if the value stays encrypted. Code reviewers can see the shape of the change.
Separate plaintext and secret variables by convention
Adopt a naming convention so it’s obvious where secrets live. The pattern I use everywhere:
group_vars/<group>/vars.yml— plaintext, human-readablegroup_vars/<group>/vault.yml— encrypted secrets only
Then in vars.yml, reference the vaulted values through an indirection:
# group_vars/production/vars.yml
database_password: "{{ vault_database_password }}"
# group_vars/production/vault.yml (encrypted)
vault_database_password: "the-real-password"
This vault_-prefix convention means a quick grep tells you every secret in the repo, and your roles never reference vaulted variables directly — they go through the clean indirection layer.
Never type the password — use a password file or script
Typing the vault password interactively is fine for one-off runs and miserable for CI. Point Ansible at a password source instead:
# ansible.cfg
[defaults]
vault_password_file = ./.vault-pass.sh
For a static password, .vault-pass.sh can just echo an environment variable:
#!/usr/bin/env bash
echo "${ANSIBLE_VAULT_PASSWORD:?vault password not set}"
In CI, inject ANSIBLE_VAULT_PASSWORD as a masked secret. Locally, developers export it from their own secret manager. The .vault-pass.sh file is committed; the actual password never is. Add the obvious guardrail:
# .gitignore
.vault-pass
*.vault-pass
Use multiple vault IDs for multiple environments
A single vault password for every environment means a leaked dev password exposes production. Vault IDs fix this — each environment gets its own password:
ansible-vault encrypt_string \
--vault-id prod@prompt 'prod-secret' --name 'db_password'
Run with the IDs you need:
ansible-playbook site.yml \
--vault-id dev@.dev-pass.sh \
--vault-id prod@.prod-pass.sh
Ansible tries each vault ID until one decrypts the file. Production engineers hold the prod password; everyone gets dev. This single change has saved more than one team from a “wait, the contractor had the prod vault key?” conversation.
Rotating the vault password
When someone leaves or a password is suspected leaked, rekey everything in one command:
ansible-vault rekey \
--new-vault-id prod@prompt \
group_vars/production/vault.yml
rekey re-encrypts with a new password without changing the underlying secrets. The catch: this only rotates the vault password, not the secrets themselves. If a credential actually leaked, you have to rotate the credential at its source (rotate the DB password, revoke the token) — re-encrypting the same value protects nothing. Treat rekey and credential-rotation as two separate tasks.
Where AI fits
AI is good at the tedious, error-prone scaffolding around Vault — and bad at the part where secrets are involved. Keep that line sharp.
Good uses:
- Generating the indirection layer. Paste a plaintext vars file and ask the model to split it into
vars.yml(withvault_-prefixed references) and avault.ymlskeleton. It’s mechanical refactoring the model does cleanly. - Writing the password-file script and CI wiring. “Write a
vault_password_filescript that reads fromANSIBLE_VAULT_PASSWORDand fails loudly if unset” is a one-shot answer. - Auditing for leaks. Ask it to scan a diff for plaintext secrets that should be vaulted. It’s a useful second pair of eyes before you push.
The hard rule: never paste a real secret into a model. Use placeholders. The model helps you build the structure that holds secrets; it never needs to see the secrets themselves. We keep a set of IaC prompts for exactly this kind of scaffolding.
A workflow that holds up
Putting it together, the setup I hand to teams looks like this:
vault_-prefixed secrets invault.yml, clean references invars.yml.encrypt_stringfor inline values so diffs stay reviewable.- A committed
vault_password_filescript reading from an env var; the password itself never in the repo. - Per-environment vault IDs so dev and prod passwords are different.
- A documented
rekeyprocedure that’s paired with actual credential rotation.
None of this is exotic. It’s just the difference between Vault being a quiet, boring part of your pipeline and being the thing that leaks your prod database password into a public repo three years from now.
AI-generated Ansible scaffolding is assistive, not authoritative. Review every change and never expose real secrets to a model.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.