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.

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 rekey changes 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.

  • Ansible
  • Security
  • DevOps