NovuSpark
All articles

May 8, 2026 · NovuSpark Team

Ansible Vault: Managing Secrets Safely

This is the fourth post in our Ansible fundamentals series, building on variables and facts.

Every Ansible codebase eventually needs to manage a secret — a database password, an API key, a TLS private key — as a variable, the same way it manages everything else. The problem is that "the same way it manages everything else" usually means a plaintext value in a YAML file, and YAML files get committed to git. Ansible Vault is the built-in answer: it encrypts a file, or a specific value, so it can live in version control safely.

Encrypting a whole file

ansible-vault create group_vars/production/vault.yml

This opens an editor, and whatever you save is encrypted on disk:

# group_vars/production/vault.yml (before encryption, as you type it)
vault_db_password: "S3cur3-Actual-Password"
vault_api_key: "sk-abc123..."

What's actually written to disk is unreadable without the vault password:

$ANSIBLE_VAULT;1.1;AES256
66386439653236336462626566653063336164663966303231363934653561363964
3633626534306535646432653766386130663534366231610a626438346336353833
...

To edit it again later:

ansible-vault edit group_vars/production/vault.yml

And to view it without opening an editor:

ansible-vault view group_vars/production/vault.yml

The pattern that actually matters: vault variables vs. regular variables

Encrypting the entire file that also contains your non-secret configuration means every trivial change — bumping a worker count, adjusting a timeout — requires touching an encrypted file, which makes diffs unreadable in a pull request and makes "what actually changed" much harder to review.

The pattern most Ansible codebases converge on instead: keep an encrypted file containing only secrets, with a consistent naming convention (a vault_ prefix is the common one), and a plaintext file that references those encrypted values by name.

# group_vars/production/vault.yml (encrypted)
vault_db_password: "S3cur3-Actual-Password"
vault_api_key: "sk-abc123..."
# group_vars/production/vars.yml (plaintext, committed as-is)
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"

Tasks reference db_password, never vault_db_password directly. The result: the plaintext file is fully readable and diffable in every pull request — you can see exactly which variable names changed — while the actual secret values stay encrypted in a separate file that rarely needs to change and rarely needs reviewing beyond "did the right person update it."

Running a playbook that needs Vault-encrypted variables

ansible-playbook -i inventory.ini site.yml --ask-vault-pass

This prompts for the vault password interactively — fine for a human running it locally, unworkable for CI. Two more practical options:

# A password file (add this file to .gitignore — never commit it)
ansible-playbook -i inventory.ini site.yml --vault-password-file ~/.vault_pass.txt
# A script that fetches the password from a secrets manager at run time
ansible-playbook -i inventory.ini site.yml --vault-password-file get-vault-pass.sh

That second form is what most real CI pipelines actually use: --vault-password-file accepts any executable, not just a static file, so the vault password itself can come from AWS Secrets Manager, HashiCorp Vault, or your CI platform's own secret store — meaning the Ansible Vault password is never itself sitting in a file on disk anywhere.

Encrypting a single value, not a whole file

For a secret embedded inside an otherwise-plaintext file, ansible-vault encrypt_string encrypts just that one value inline:

ansible-vault encrypt_string 'S3cur3-Actual-Password' --name 'db_password'
db_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          66386439653236336462626566653063336164663966303231363934653561
          ...

That block can be pasted directly into an otherwise-normal, plaintext YAML file — most of the file stays readable, and only the specific encrypted values are unreadable without the vault password. This is a reasonable middle ground for a file that's mostly non-sensitive configuration with one or two genuinely sensitive values mixed in.

What Vault does not solve

Vault encrypts secrets at rest in your repository. It does not:

  • Protect a decrypted value in memory or in debug output during a run. A task that prints db_password for debugging purposes prints the real, decrypted value — Vault has already done its job of protecting the file by the time a task runs.
  • Replace a dedicated secrets manager for rotation and access control. Vault has no concept of automatic rotation, per-user access grants, or an audit log of who read which secret when. For an organization with real secrets-management maturity requirements, Ansible Vault is usually the mechanism that hands off to a real secrets manager at run time (as in the CI example above), not a full replacement for one.
  • Make the vault password itself safe to lose. Whoever holds that one password can decrypt everything. Treat it exactly like the master credential it is — the same category of thing as the AWS credentials we discussed protecting in our own CI/CD case study.

What to actually remember from this post

  • ansible-vault create / edit / view manage whole encrypted files; encrypt_string encrypts a single value inline.
  • Keep secrets and regular configuration in separate files, linked by a naming convention — it's the difference between a readable pull request and an opaque one.
  • --vault-password-file accepts a script, which is what makes Vault usable from CI without a human typing a password interactively.
  • Vault protects secrets at rest in git — it's not a substitute for a real secrets manager if your organization needs rotation, granular access control, or an audit trail.

Next in the series: Idempotency and Testing Ansible Playbooks with Molecule, where we cover how to actually verify a playbook does what you think it does, before it runs against real infrastructure.

Ready when you are

Want training built around your team's real work?

Tell us about your team and what you're trying to solve — we'll recommend a program that fits.