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