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 commitvault.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:
- Ask DevOps team lead (via Rocket.Chat DM)
- Store in
~/.ansible_vault_password(local machine only) - 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):
- Go to repository Settings → Secrets → Actions
- Add secret:
ANSIBLE_VAULT_PASSWORD - 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-passinstead 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 -1shows$ANSIBLE_VAULT...) - Vault password is NOT in the same repo
-
.gitignoreincludes 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
Related Documentation
- GitHub Secrets Guide - For CI/CD secrets
- Secret Rotation - Rotating compromised secrets
- Infrastructure Standards - Ansible best practices