Introduction

Ansible Vault encrypts sensitive data — passwords, API keys, certificates, and configuration files — so you can safely store them in version control alongside your playbooks. It uses AES-256 symmetric encryption and integrates seamlessly into playbook execution.

This guide covers all Vault operations: encrypting strings, files, using multiple vault passwords, and best practices for managing secrets in production.

How Ansible Vault Works

Vault encrypts data using AES-256 in CTR mode with HMAC-SHA256 authentication. The encrypted content is stored as ASCII-armored text prefixed with $ANSIBLE_VAULT;1.1;AES256.

Key concepts:

  • Vault encrypts at rest — decryption happens in memory during playbook execution
  • A single vault password (passphrase) encrypts/decrypts the content
  • Multiple vault IDs allow different passwords for different secrets
  • Vault integrates with ansible-playbook, ansible, and ansible-vault CLI tools

Basic Operations

Encrypt a File

# Interactive password prompt
$ ansible-vault encrypt vars/secrets.yml
New Vault password: 
Confirm New Vault password: 
Encryption successful

# Using a password file
$ ansible-vault encrypt vars/secrets.yml --vault-password-file ~/.vault_pass
Encryption successful

Before encryption:

---
db_password: "SuperSecret123!"
api_key: "sk-abc123def456"
ssl_private_key: |
  -----BEGIN RSA PRIVATE KEY-----
  MIIEpAIBAAKCAQEA...

After encryption:

$ANSIBLE_VAULT;1.1;AES256
36353832316130613239633365656232653262353632...

Decrypt a File

# View decrypted content (doesn't modify file)
$ ansible-vault view vars/secrets.yml

# Decrypt file back to plaintext (modifies file)
$ ansible-vault decrypt vars/secrets.yml

# Decrypt to stdout
$ ansible-vault decrypt vars/secrets.yml --output -

Edit an Encrypted File

# Opens in $EDITOR, re-encrypts on save
$ ansible-vault edit vars/secrets.yml

Change Vault Password

$ ansible-vault rekey vars/secrets.yml
Vault password (current): 
New Vault password: 
Confirm New Vault password: 
Rekey successful

Encrypt Individual Strings (encrypt_string)

For encrypting a single variable value without encrypting the entire file:

# Encrypt a string for use in YAML
$ ansible-vault encrypt_string 'SuperSecret123!' --name 'db_password'
db_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  62313637323663343764643830...

# From stdin (avoids password in shell history)
$ echo -n 'SuperSecret123!' | ansible-vault encrypt_string --stdin-name 'db_password'

# With a vault ID
$ ansible-vault encrypt_string 'mykey123' --name 'api_key' --vault-id prod@~/.vault_pass_prod

Use in Variable Files

---
# vars/secrets.yml - mix of plain and encrypted values
app_name: myapp
app_port: 8080
db_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  62313637323663343764643830353433363938636431626563653531376533306662
  3237366261616130323035353163613436393337326137360a643931613762363130
  63396332363736363236623139326233383262356337633464306439613138663235
  3530336538346637650a393263303931333233313632366363643062653262356365
  6264
api_key: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  35376233393338613463616430303334...

Using Vault in Playbooks

Running a Playbook with Vault

# Interactive password prompt
$ ansible-playbook deploy.yml --ask-vault-pass

# Password file
$ ansible-playbook deploy.yml --vault-password-file ~/.vault_pass

# Environment variable pointing to password file
$ export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass
$ ansible-playbook deploy.yml

Playbook Example

---
- name: Deploy with encrypted secrets
  hosts: dbservers
  become: true
  vars_files:
    - vars/common.yml
    - vars/secrets.yml  # Can be fully encrypted
  tasks:
    - name: Configure database password
      ansible.builtin.template:
        src: db.conf.j2
        dest: /etc/myapp/db.conf
        mode: '0600'
      # {{ db_password }} is decrypted automatically

    - name: Set environment variable
      ansible.builtin.lineinfile:
        path: /etc/myapp/env
        line: "API_KEY={{ api_key }}"
        mode: '0600'

Multiple Vault Passwords (Vault IDs)

For organizations managing secrets across environments:

# Encrypt with vault ID "prod"
$ ansible-vault encrypt vars/prod-secrets.yml --vault-id prod@~/.vault_pass_prod

# Encrypt with vault ID "dev"  
$ ansible-vault encrypt vars/dev-secrets.yml --vault-id dev@~/.vault_pass_dev

# Run playbook with multiple vault IDs
$ ansible-playbook site.yml \
    --vault-id dev@~/.vault_pass_dev \
    --vault-id prod@~/.vault_pass_prod

ansible.cfg Configuration

[defaults]
vault_identity_list = dev@~/.vault_pass_dev, prod@~/.vault_pass_prod

Best Practices

1. Separate Secret and Non-Secret Variables

group_vars/
├── production/
│   ├── vars.yml          # Non-sensitive (plain text)
│   └── vault.yml         # Sensitive (encrypted)
└── staging/
    ├── vars.yml
    └── vault.yml

2. Prefix Vaulted Variable Names

# vault.yml (encrypted)
---
vault_db_password: "SuperSecret123!"
vault_api_key: "sk-abc123"

# vars.yml (plain text, references vault vars)
---
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"

This makes it clear which variables contain secrets.

3. Never Commit Vault Password Files

# .gitignore
.vault_pass*
*.vault_password

4. Use a Password Script

#!/bin/bash
# ~/.vault_pass_script.sh
# Fetch password from a secret manager
aws secretsmanager get-secret-value --secret-id ansible-vault --query SecretString --output text
[defaults]
vault_password_file = ~/.vault_pass_script.sh

5. Encrypt Only What's Necessary

Don't encrypt entire files if only one variable is sensitive — use encrypt_string for individual values.

Complete Playbook: Secret Rotation

---
- name: Rotate database credentials
  hosts: dbservers
  become: true
  vars_files:
    - vars/vault.yml
  vars:
    new_password: "{{ lookup('password', '/dev/null length=24 chars=ascii_letters,digits') }}"
  tasks:
    - name: Update database user password
      community.mysql.mysql_user:
        name: "{{ db_username }}"
        password: "{{ new_password }}"
        login_user: root
        login_password: "{{ vault_mysql_root_password }}"
        state: present

    - name: Update application config
      ansible.builtin.template:
        src: database.yml.j2
        dest: /opt/app/config/database.yml
        owner: appuser
        mode: '0600'
      vars:
        db_password: "{{ new_password }}"
      notify: Restart application

    - name: Store new password in vault (local)
      ansible.builtin.copy:
        content: |
          ---
          vault_db_password: "{{ new_password }}"
        dest: "{{ playbook_dir }}/vars/vault.yml"
        mode: '0600'
      delegate_to: localhost
      run_once: true

    - name: Re-encrypt vault file
      ansible.builtin.command:
        cmd: ansible-vault encrypt "{{ playbook_dir }}/vars/vault.yml"
      delegate_to: localhost
      run_once: true
      changed_when: true

  handlers:
    - name: Restart application
      ansible.builtin.systemd:
        name: myapp
        state: restarted

Troubleshooting

"Decryption failed"

Cause: Wrong vault password.

Fix: Verify you're using the correct password file or prompt.

"is not a vault encrypted file"

Cause: File doesn't start with $ANSIBLE_VAULT header.

Fix: Ensure the file was encrypted with ansible-vault encrypt.

"ERROR! Vault password required but not provided"

Fix: Add --ask-vault-pass or --vault-password-file to your command:

ansible-playbook site.yml --ask-vault-pass

Viewing Encrypted Variables in Debug

- name: Debug vaulted variable (shows decrypted value!)
  ansible.builtin.debug:
    var: db_password
  # WARNING: This prints the secret in plain text to console
  tags: [never, debug]  # Only run when explicitly tagged

Vault vs External Secret Managers

FeatureAnsible VaultHashiCorp VaultAWS Secrets Manager
Setup complexityLowHighMedium
Dynamic secretsNoYesYes
Access controlFile-basedPolicy-basedIAM-based
RotationManualAutomaticAutomatic
CostFree$$$$ per secret
Best forSmall teamsEnterpriseAWS-native

For larger organizations, consider combining Ansible Vault with a lookup plugin:

- name: Fetch secret from HashiCorp Vault
  ansible.builtin.debug:
    msg: "{{ lookup('hashi_vault', 'secret=secret/data/myapp:password') }}"

Conclusion

Ansible Vault is the simplest way to manage secrets in Ansible — no external infrastructure required. Use encrypt_string for individual variables, full file encryption for secret-heavy files, and vault IDs for multi-environment setups. Always store vault passwords outside version control and consider password scripts that integrate with your team's existing secret management tools.