Introduction

Ansible Vault encrypts sensitive data — passwords, API keys, certificates, tokens — so you can safely store them in version control alongside your playbooks. Files are encrypted with AES-256, and you can encrypt entire files or individual variables (inline encryption).

Create an Encrypted File

# Create a new encrypted file
ansible-vault create secrets.yml
# Opens your editor — type your secrets, save, and it's encrypted

# Create with specific vault ID
ansible-vault create --vault-id prod@prompt secrets.yml

secrets.yml (what you type inside the editor):

db_password: "SuperSecret123!"
api_key: "sk-abc123def456"
ssl_private_key: |
  -----BEGIN PRIVATE KEY-----
  MIIEvQIBADANBgkqhkiG9w0BAQE...
  -----END PRIVATE KEY-----

The saved file looks like:

$ANSIBLE_VAULT;1.1;AES256
65316332393532313030636134643235623365...

Encrypt an Existing File

# Encrypt an existing file
ansible-vault encrypt vars/production.yml

# Encrypt multiple files
ansible-vault encrypt vars/production.yml vars/staging.yml

# Encrypt with vault ID label
ansible-vault encrypt --vault-id prod@prompt vars/production.yml

View, Edit, Decrypt

# View encrypted file (read-only)
ansible-vault view secrets.yml

# Edit encrypted file (opens in editor)
ansible-vault edit secrets.yml

# Decrypt file permanently (removes encryption)
ansible-vault decrypt secrets.yml

# Re-key (change password)
ansible-vault rekey secrets.yml

Encrypt a Single String

Instead of encrypting the whole file, encrypt just one value:

# Encrypt a string
ansible-vault encrypt_string 'SuperSecret123!' --name 'db_password'

Output (paste this into your vars file):

db_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  65316332393532313030636134643235623365...
# Encrypt from stdin (avoid password in shell history)
echo -n 'SuperSecret123!' | ansible-vault encrypt_string --stdin-name 'db_password'

# Encrypt with specific vault ID
ansible-vault encrypt_string 'mytoken' --name 'api_token' --vault-id prod@prompt

Use Inline Encrypted Variables

# vars/main.yml — mix encrypted and plain vars
---
app_name: myapp
app_port: 8080
db_host: db.internal

# This value is encrypted, everything else is plain
db_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  65316332393532313030636134643235623365...

api_key: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  38373661326462303562323564393030...

Run Playbooks with Vault

# Prompt for password
ansible-playbook site.yml --ask-vault-pass

# Password from file
ansible-playbook site.yml --vault-password-file ~/.vault_pass

# Password from script (dynamic)
ansible-playbook site.yml --vault-password-file ./get-vault-pass.sh

# Environment variable
export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass
ansible-playbook site.yml

Password File

# Create a password file
echo 'MyVaultPassword' > ~/.vault_pass
chmod 600 ~/.vault_pass

# Set in ansible.cfg
# [defaults]
# vault_password_file = ~/.vault_pass

⚠️ Never commit the password file. Add it to .gitignore:

# .gitignore
.vault_pass
*.vault_pass

Multi-Vault (Multiple Passwords)

Use different passwords for different environments:

# Encrypt with vault IDs
ansible-vault encrypt --vault-id dev@prompt vars/dev.yml
ansible-vault encrypt --vault-id prod@prompt vars/prod.yml

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

Practical Examples

Separate Secrets File

project/
├── playbook.yml
├── vars/
│   ├── main.yml          # Plain variables
│   └── secrets.yml       # Encrypted (ansible-vault)
└── .gitignore            # Contains .vault_pass

vars/main.yml:

app_name: myapp
app_port: 8080
db_host: db.internal
db_name: myapp_prod

vars/secrets.yml (encrypted):

db_password: "SuperSecret123!"
api_key: "sk-abc123def456"
smtp_password: "EmailPass789"

playbook.yml:

---
- name: Deploy application
  hosts: appservers
  become: true
  vars_files:
    - vars/main.yml
    - vars/secrets.yml  # Ansible decrypts automatically

  tasks:
    - name: Deploy config
      ansible.builtin.template:
        src: app-config.yml.j2
        dest: /opt/myapp/config.yml
        mode: "0640"

Group Vars with Vault

inventory/
├── hosts
├── group_vars/
│   ├── all/
│   │   ├── vars.yml       # Plain vars for all hosts
│   │   └── vault.yml      # Encrypted secrets for all hosts
│   ├── production/
│   │   ├── vars.yml
│   │   └── vault.yml      # Production secrets
│   └── staging/
│       ├── vars.yml
│       └── vault.yml      # Staging secrets

This pattern is recommended by Ansible documentation — Ansible automatically loads both vars.yml and vault.yml from group_vars directories.

Encrypt Certificates

# Encrypt a certificate file
ansible-vault encrypt files/ssl/server.key

# Use in playbook
- name: Deploy SSL key
  ansible.builtin.copy:
    src: ssl/server.key   # Ansible decrypts automatically
    dest: /etc/ssl/private/server.key
    mode: "0600"

CI/CD Integration

# GitHub Actions
- name: Run Ansible
  env:
    ANSIBLE_VAULT_PASSWORD: ${{ secrets.VAULT_PASSWORD }}
  run: |
    echo "$ANSIBLE_VAULT_PASSWORD" > .vault_pass
    ansible-playbook site.yml --vault-password-file .vault_pass
    rm -f .vault_pass

# GitLab CI
deploy:
  script:
    - echo "$VAULT_PASSWORD" > .vault_pass
    - ansible-playbook site.yml --vault-password-file .vault_pass
    - rm -f .vault_pass

Best Practices

  1. Separate secrets from plain vars — keep encrypted files small and focused
  2. Use vault.yml naming convention — makes it obvious which files are encrypted
  3. Never commit the vault password — use .gitignore, CI secrets, or a password manager
  4. Use inline encryption for 1-2 secrets mixed with plain vars
  5. Use file encryption when most/all values in a file are sensitive
  6. Use vault IDs when you have different passwords per environment
  7. Rotate passwords regularly with ansible-vault rekey

Troubleshooting

"Decryption failed"

Wrong password. Verify you're using the correct password/file:

# Test by viewing
ansible-vault view secrets.yml --ask-vault-pass

"is not a vault encrypted file"

The file isn't encrypted, or the header is corrupted. Check if it starts with $ANSIBLE_VAULT;:

head -1 secrets.yml
# Should show: $ANSIBLE_VAULT;1.1;AES256

"No vault secrets found"

You forgot --ask-vault-pass or --vault-password-file:

# Add the flag
ansible-playbook site.yml --ask-vault-pass

# Or set in ansible.cfg
# [defaults]
# vault_password_file = ~/.vault_pass

Conclusion

Ansible Vault makes it safe to store secrets in Git. Encrypt entire files with ansible-vault encrypt or individual values with encrypt_string. Use password files or CI/CD secrets for automation. The recommended pattern: keep plain vars in vars.yml and secrets in vault.yml within group_vars directories. Use multi-vault with vault IDs when managing multiple environments with different passwords.