Ansible Vault: Managing Secrets

K
Kai··4 min read

Every real project has secrets: database passwords, API keys, certificates. But playbooks and variables usually go into Git — and leaving secrets in plaintext in Git is a serious security mistake (recall the Docker series, Article 9 — don't embed secrets). Ansible Vault solves this: encrypt secrets right in the project, decrypt automatically at run time.

The problem and the solution

You have group_vars/web.yml containing db_password: "SieuBiMat123!". Commit it to Git → anyone viewing the repo sees the password. Pulling the secret out into the environment makes versioning hard.

Ansible Vault encrypts a file/value with AES256: the encrypted file still lives in the repo (safe to commit since it's encrypted), and Ansible decrypts it in memory at run time if you provide the vault password. Secrets are version-controlled without being exposed.

Encrypt a secret file

Create a secret file, then encrypt it:

ansible-vault encrypt secrets.yml

(It prompts you to set a vault password.) The file after encryption:

head -2 secrets.yml
$ANSIBLE_VAULT;1.1;AES256
65346263623637653437663938653262356666323362626432623136...

The first line $ANSIBLE_VAULT;1.1;AES256 marks the file as Vault-encrypted, and below it is the encrypted data (hex). The original content is completely unreadable — safe to commit.

Working with an encrypted file

ansible-vault view secrets.yml      # view content (decrypt to screen, no edit)
ansible-vault edit secrets.yml      # open editor, edit (auto-decrypt then re-encrypt on save)
ansible-vault decrypt secrets.yml   # decrypt PERMANENTLY to a plain file (careful!)
ansible-vault rekey secrets.yml     # change the vault password
ansible-vault create secrets.yml    # create a new encrypted file directly

view:

db_password: "SieuBiMat123!"
api_key: "sk-abc123xyz"

edit is the most-used command: it decrypts into a temp file, opens the editor (Article 4 of the Linux series), then re-encrypts when you save — you never see the decrypted file on disk.

encrypt_string: encrypt a single value

Sometimes you only want to encrypt one variable and place it next to plain variables (no separate file needed). Use encrypt_string:

ansible-vault encrypt_string "secret-token" --name "my_token"
my_token: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          32393835343465623936353162333664316233646337663261...

Paste this !vault | ... block straight into a plain variable YAML file — only that value is encrypted, the others stay readable. Handy when most variables aren't sensitive and only a few need hiding.

Use vaulted vars in a playbook

A vaulted file is used like a plain variable file (vars_files), you just provide the password at run time:

- hosts: web
  vars_files:
    - group_vars/web_secrets.yml      # encrypted file
  tasks:
    - ansible.builtin.debug:
        msg: "db_password is {{ db_password | length }} characters long"

Run it, providing the password:

ansible-playbook vault-demo.yml --ask-vault-pass         # type the password interactively
# or:
ansible-playbook vault-demo.yml --vault-password-file .vault_pass   # read from a file
"msg": "db_password is 13 characters long"

Ansible automatically decrypts web_secrets.yml in memory and db_password works like a plain variable. (Here we only print the length so as not to expose the value — a good habit in demos/logs.)

Vault password: how to provide it

  • --ask-vault-pass — type the password each run. Safest for manual operations.
  • --vault-password-file <file> — read the password from a file. Convenient for automation/CI — but that file must stay out of Git (add it to .gitignore), with 600 permissions, and ideally generated from a secret manager at run time.
  • Preset it in ansible.cfg: vault_password_file = .vault_pass to skip the flag.

Vault id (advanced): many environments with different passwords — --vault-id prod@prompt --vault-id dev@dev-pass. Lets you separate prod/dev secrets with their own keys.

Best practices

  • Encrypt only what needs encrypting: split secrets into group_vars/<group>/vault.yml (encrypted) and plain variables into group_vars/<group>/vars.yml (clear). Encrypting a whole plain-variable file makes it hard to read/diff.
  • The vault password does NOT go into Git: gitignore the password file; don't hard-code it in a playbook.
  • Diff-friendly: configure git to show diffs of vaulted files (ansible-vault supports this), or accept that the diff is encrypted data.
  • Consider an external secret manager: at large scale, use HashiCorp Vault / AWS Secrets Manager (via a lookup plugin — Article 12) instead of storing encrypted secrets in the repo. Ansible Vault suits static config secrets; a secret manager suits rotating/dynamic secrets.

🧹 Cleanup

The web_secrets.yml file (encrypted) is safe to commit to the nghiadaulau/ansible-series repo, directory 10-vault; the password file .vault_pass is not (it's gitignored).

Wrap-up

Ansible Vault encrypts secrets with AES256 for safe version control — the encrypted file ($ANSIBLE_VAULT;...) is committable, decrypted automatically at run time. Operations: encrypt/view/edit/rekey, and encrypt_string for individual values. Use a vaulted file like vars_files, providing the password via --ask-vault-pass or --vault-password-file (which must be outside Git). Best practice: separate secrets from plain variables, keep the vault password safe, consider an external secret manager at large scale.

So far we've used built-in modules. Article 11 is a real deep dive: writing your own module in Python — once you understand this, you've grasped the entire Ansible mechanism from Article 1.