Storing plain-text database passwords, API tokens, or SSH private keys inside Git repositories is a severe security violation that leads to credential leaks and compliance failures.
Ansible Vault provides built-in AES-256 encryption for variables and files, allowing sensitive secrets to be safely committed to version control while remaining fully accessible to automated execution pipelines.
Topic 1: How Ansible Vault Works
- At Rest (In Git): Secrets are stored as encrypted ciphertext blocks starting with header
$ANSIBLE_VAULT;1.1;AES256. - During Execution: Ansible prompts for the Vault password (or reads a password file), decrypts the secrets strictly in memory, and injects them into playbook execution without writing decrypted files to target disks.
- Log Suppression: Ansible automatically redacts vault-encrypted values from terminal output logs.
Topic 2: Encrypting Whole Files vs. Inline String Variables
Ansible Vault supports two primary encryption workflows:
1. Whole File Encryption (group_vars/vault.yml)
Encrypts an entire YAML file containing sensitive key-value pairs:
# Create a new encrypted file
ansible-vault create group_vars/production/vault.yml
# View or Edit an existing encrypted file
ansible-vault view group_vars/production/vault.yml
ansible-vault edit group_vars/production/vault.yml
# Rekey an encrypted file with a new password
ansible-vault rekey group_vars/production/vault.yml
# Inside group_vars/production/vault.yml (Encrypted File Content)
vault_db_password: "ProdDBSecretPassword2026!"
vault_api_token: "sk-live-948291038291038"
vault_jwt_secret: "super-secret-jwt-key"
2. Inline String Variable Encryption (encrypt_string)
Encrypts a single sensitive string inline inside an otherwise unencrypted YAML variable file. This keeps variable keys readable in Git while hiding only the secret value:
# Encrypt an individual string inline
ansible-vault encrypt_string 'ProdDBSecretPassword2026!' --name 'db_password'
# Inside group_vars/production/vars.yml (Unencrypted file containing inline Vault string)
db_host: "10.0.2.20"
db_port: 5432
db_user: "postgres"
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
61386566366632373030386230353434616239383637373531336435346338393539316630386532
3737303738363236313164393937303965383561333734340a323330363237323838383838383838
Topic 3: Vault Password Files & Multiple Vault IDs
In automated CI/CD pipelines (Jenkins, GitHub Actions, GitLab CI), interactive password prompts cannot be used.
1. Vault Password File
Supply a file containing the vault password via command line or ansible.cfg:
# Inside ansible.cfg
[defaults]
vault_password_file = ~/.ansible/.vault_pass
# Executing playbook using vault password file
ansible-playbook -i hosts.ini site.yml --vault-password-file ~/.ansible/.vault_pass
2. Multiple Vault IDs (Multi-Environment Secret Keys)
In enterprise organizations, different teams or environments (Dev vs. Staging vs. Prod) use different encryption keys. Ansible supports Multiple Vault IDs:
# Create dev and prod secrets with distinct Vault IDs
ansible-vault create --vault-id dev@prompt group_vars/dev/vault.yml
ansible-vault create --vault-id prod@prompt group_vars/prod/vault.yml
# Execute playbook supplying specific vault password files per ID
ansible-playbook -i hosts.ini site.yml \
--vault-id dev@~/.vault_dev \
--vault-id prod@~/.vault_prod
Topic 4: Redacting Logs with no_log: true
Even when using Ansible Vault, if a task prints a variable in debug output or executes a command containing a password argument, the secret might be logged in plain text to execution stdout!
To prevent log exposure, add no_log: true to any task handling sensitive data:
- name: Log in to Docker registry with secret API token
community.docker.docker_login:
registry_url: https://index.docker.io/v1/
username: devops
password: "{{ vault_docker_token }}"
no_log: true # Hides task parameters and stdout from execution logs!
Common mistake: Encrypting an entire group_vars file so nobody can review it. The diff on every change is one unreadable blob, and reviewers approve it blind. Encrypt individual values with ansible-vault encrypt_string so the file stays diffable and only the secret is opaque — and remember no_log: true on the tasks that would otherwise print it.