Skip to main content

Ansible Vault Guide

What is Ansible Vault?

Ansible Vault encrypts sensitive data (passwords, API keys, certificates) in your infrastructure code, allowing you to commit encrypted secrets to Git safely.

How It Works

Plain text secret → Ansible Vault encryption → Encrypted file in Git

Ansible playbook run

Vault password provided

Secret decrypted in memory

Used in deployment

Key Point: The encrypted file is safe to commit to Git. The vault password is stored in GitHub Secrets for CI/CD.

File Structure

infrastructure/
├── inventory/
│ ├── production/
│ │ ├── hosts.yml
│ │ └── group_vars/
│ │ ├── all/
│ │ │ ├── vars.yml # Plain-text variables
│ │ │ └── vault.yml # ENCRYPTED secrets
│ │ ├── web_servers/
│ │ │ ├── vars.yml
│ │ │ └── vault.yml # ENCRYPTED
│ ├── staging/
│ │ └── group_vars/...
│ └── development/
│ └── group_vars/...

Naming convention:

  • vars.yml - Plain-text, safe to commit
  • vault.yml - Encrypted with Ansible Vault, safe to commit

Creating Encrypted Secrets

Method 1: Create New Encrypted File

# Create and encrypt a new file
ansible-vault create inventory/production/group_vars/all/vault.yml

You'll be prompted for a vault password. Then your editor opens:

---
# Production secrets - encrypted with Ansible Vault
vault_db_password: "super_secret_production_password"
vault_api_key: "sk-prod-1234567890abcdef"
vault_ssl_private_key: |
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEF...
-----END PRIVATE KEY-----

Save and exit. File is now encrypted.

Method 2: Encrypt Existing File

# Already have a plain-text file with secrets?
ansible-vault encrypt inventory/production/group_vars/all/vault.yml

Method 3: Encrypt a String

# Encrypt a single value
ansible-vault encrypt_string 'my_secret_password' --name 'vault_db_password'

Output:

vault_db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66386439653236336462626566653063336164663966303231363934653561363964363833
...

Copy this into your vault.yml file.

Using Encrypted Secrets

Reference in Plain-Text Vars

# inventory/production/group_vars/all/vars.yml (plain-text)
db_host: "prod-db.internal"
db_port: 5432
db_user: "appuser"
db_password: "{{ vault_db_password }}" # References encrypted var

api_base_url: "https://api.company.com"
api_key: "{{ vault_api_key }}" # References encrypted var
# inventory/production/group_vars/all/vault.yml (encrypted)
vault_db_password: "actual_secret_password"
vault_api_key: "actual_secret_api_key"

Principle: Reference encrypted variables from plain-text variables. This makes it clear what's secret and what's not.

Use in Playbooks

# playbooks/deploy-app.yml
- name: Deploy application
hosts: web_servers
tasks:
- name: Create database config
template:
src: database.conf.j2
dest: /etc/app/database.conf
vars:
db_connection_string: "postgresql://{{ db_user }}:{{ db_password }}@{{ db_host }}:{{ db_port }}/appdb"

Running Playbooks with Vault

Interactive (Local Development)

# Ansible will prompt for vault password
ansible-playbook playbooks/deploy.yml --ask-vault-pass

With Password File (Local)

# Store vault password in a file (DO NOT COMMIT THIS)
echo "my_vault_password" > ~/.ansible_vault_password
chmod 600 ~/.ansible_vault_password

# Run playbook
ansible-playbook playbooks/deploy.yml --vault-password-file ~/.ansible_vault_password

Add to .gitignore:

.ansible_vault_password
vault-password.txt
*.vault_pass

In CI/CD (GitHub Actions)

# .github/workflows/deploy.yml
- name: Deploy to production
env:
ANSIBLE_VAULT_PASSWORD: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
run: |
echo "$ANSIBLE_VAULT_PASSWORD" > /tmp/vault_pass
ansible-playbook playbooks/deploy.yml \
--vault-password-file /tmp/vault_pass \
--inventory inventory/production/hosts.yml
rm /tmp/vault_pass # Clean up

Setup: Add vault password to GitHub Secrets as ANSIBLE_VAULT_PASSWORD.

Editing Encrypted Files

View Encrypted File

# View contents without decrypting to disk
ansible-vault view inventory/production/group_vars/all/vault.yml

Edit Encrypted File

# Edit file in your default editor
ansible-vault edit inventory/production/group_vars/all/vault.yml

Ansible decrypts to temp location, opens editor, re-encrypts on save.

Decrypt for Editing

# Decrypt to plain-text (BE CAREFUL)
ansible-vault decrypt inventory/production/group_vars/all/vault.yml

# Edit the file...

# Re-encrypt before committing
ansible-vault encrypt inventory/production/group_vars/all/vault.yml

Warning: Never commit decrypted vault.yml files!

Vault Password Management

For Local Development

Developers need the vault password to run Ansible locally.

How to get it:

  1. Ask DevOps team lead (via Rocket.Chat DM)
  2. Store in ~/.ansible_vault_password (local machine only)
  3. Never commit to Git

Alternative: Use different vault passwords for different environments:

  • Production vault password → Only DevOps team + CI/CD
  • Staging vault password → All engineers

For CI/CD

GitHub Actions: Vault password stored in GitHub Secrets.

Setup (DevOps admin only):

  1. Go to repository Settings → Secrets → Actions
  2. Add secret: ANSIBLE_VAULT_PASSWORD
  3. Value: Your vault password

Multiple environments:

secrets:
ANSIBLE_VAULT_PASSWORD_PROD: "production_vault_password"
ANSIBLE_VAULT_PASSWORD_STAGING: "staging_vault_password"

Rotating Vault Password

When to rotate:

  • Employee with access leaves company
  • Password may have been exposed
  • Regular rotation (annually)

How to rotate:

# Re-key all vault files with new password
ansible-vault rekey inventory/production/group_vars/all/vault.yml

You'll be prompted for old password, then new password.

For multiple files:

# Find all vault files and rekey them
find inventory/production -name "vault.yml" -exec ansible-vault rekey {} \;

Update GitHub Secret with new password.

Best Practices

Variable Naming Convention

Prefix all encrypted variables with vault_:

# vault.yml
vault_db_password: "secret"
vault_api_key: "secret"
vault_ssl_cert: "secret"

# vars.yml (references)
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"
ssl_cert: "{{ vault_ssl_cert }}"

Why: Makes it obvious which variables are encrypted.

Keep Secrets Minimal

Only encrypt truly secret data:

Encrypt:

  • Passwords
  • API keys
  • Private keys/certificates
  • OAuth tokens

Don't encrypt (use plain-text vars):

  • Hostnames
  • Ports
  • Usernames (unless sensitive)
  • Public configuration

Reason: Encrypted files are harder to diff, review, and debug.

One Vault File Per Environment

Don't mix environments in the same vault file:

✅ Good:
inventory/production/group_vars/all/vault.yml
inventory/staging/group_vars/all/vault.yml

❌ Bad:
inventory/all/vault.yml # Contains prod + staging secrets

Why: Easier to control access (different vault passwords per environment).

Document What's in Vault

Add comments to vault files (before encrypting):

---
# Production Database Credentials
vault_db_password: "secret_password"
vault_db_admin_password: "admin_password"

# Third-party API Keys
vault_stripe_api_key: "sk_live_..."
vault_sendgrid_api_key: "SG...."

# SSL Certificates (renewed annually in December)
vault_ssl_private_key: |
-----BEGIN PRIVATE KEY-----
...

Troubleshooting

Error: "Vault password incorrect"

  • Check you're using the right vault password
  • Verify password doesn't have trailing newline
  • Try --ask-vault-pass instead of password file

Error: "Attempting to decrypt but no vault secrets found"

  • File isn't encrypted (use ansible-vault encrypt)
  • File is corrupted (restore from backup)

Can't remember which files are encrypted

# Search for vault-encrypted files
find inventory/ -name "*.yml" -exec grep -l "\$ANSIBLE_VAULT" {} \;

Want to see encrypted vs plain-text values

# Show variable resolution
ansible-inventory -i inventory/production --host web-01 \
--vault-password-file ~/.ansible_vault_password

Pre-commit hook blocking vault.yml commit

  • This is expected! Verify file is encrypted first:
    head -1 inventory/production/group_vars/all/vault.yml
    # Should show: $ANSIBLE_VAULT;1.1;AES256
  • If encrypted, update pre-commit config to allow encrypted files

Security Checklist

Before committing vault.yml files:

  • File is encrypted (head -1 shows $ANSIBLE_VAULT...)
  • Vault password is NOT in the same repo
  • .gitignore includes vault password files
  • Only authorized team members have vault password
  • CI/CD has vault password in GitHub Secrets
  • Vault password is complex (20+ characters)

Examples

Complete Example: Database Credentials

1. Create encrypted vault file:

ansible-vault create inventory/production/group_vars/databases/vault.yml

2. Add secrets:

---
# PostgreSQL Admin Credentials
vault_postgres_admin_password: "very_secure_admin_password_123!"

# Application Database Credentials
vault_app_db_password: "another_secure_password_456!"

# Database Backup Encryption Key
vault_backup_encryption_key: "backup_encryption_key_789!"

3. Create plain-text vars:

# inventory/production/group_vars/databases/vars.yml
postgres_admin_user: "postgres"
postgres_admin_password: "{{ vault_postgres_admin_password }}"

app_db_user: "appuser"
app_db_password: "{{ vault_app_db_password }}"
app_db_name: "production_app"

backup_schedule: "0 2 * * *" # 2 AM daily
backup_encryption_key: "{{ vault_backup_encryption_key }}"

4. Use in playbook:

# playbooks/setup-database.yml
- name: Configure PostgreSQL
hosts: databases
tasks:
- name: Set postgres admin password
postgresql_user:
name: "{{ postgres_admin_user }}"
password: "{{ postgres_admin_password }}"
role_attr_flags: SUPERUSER

- name: Create application database user
postgresql_user:
name: "{{ app_db_user }}"
password: "{{ app_db_password }}"
db: "{{ app_db_name }}"

5. Deploy:

ansible-playbook playbooks/setup-database.yml \
-i inventory/production/hosts.yml \
--vault-password-file ~/.ansible_vault_password