Ansible for the Homelab — Part 4: Secrets Management with Ansible Vault
By now your playbooks are doing real work - bootstrapping machines, installing Docker, managing roles. At some point you're going to want to commit all of this to git. And that's when you notice the problem: your inventory and group vars contain API keys, tokens, and passwords in plain text.
Ansible Vault solves this. It encrypts sensitive values so you can safely commit everything to a repository without exposing secrets.
The Problem with Plaintext Secrets
Consider a Cloudflare role that needs an API token. The naive approach puts it directly in a vars file:
# roles/cloudflare-dns/vars/main.yml
cloudflare_api_token: "your-token-here" # ← anyone with repo access can read this
Commit that and your token is in git history forever. Even if you delete it later, it's already there.
The Split Vars Pattern
The clean solution is to keep two vars files side by side: one for non-sensitive values (committed in plain text) and one for secrets (committed encrypted).
roles/cloudflare-dns/vars/
├── main.yml ← plain text, safe to commit
└── vault.yml ← encrypted, safe to commit
In main.yml, reference the vault variable by name using a vault_ prefix convention:
# roles/cloudflare-dns/vars/main.yml
cloudflare_api_token: "{{ vault_cloudflare_api_token }}"
In vault.yml, store the actual secret:
# roles/cloudflare-dns/vars/vault.yml (before encrypting)
vault_cloudflare_api_token: "your-actual-token-here"
Ansible loads both files automatically and resolves the reference at runtime. Your real token never appears outside the encrypted file.
Encrypting the Vault File
Once you've written vault.yml with the real values, encrypt it:
ansible-vault encrypt roles/cloudflare-dns/vars/vault.yml
You'll be prompted to create a vault password. After encryption, the file becomes unreadable ciphertext - but it's still a valid YAML file as far as git is concerned. Commit it freely.
To view or edit an encrypted file later:
ansible-vault view roles/cloudflare-dns/vars/vault.yml
ansible-vault edit roles/cloudflare-dns/vars/vault.yml
Running Playbooks with Vault
When you run a playbook that uses encrypted files, Ansible needs the vault password. The simplest way:
ansible-playbook playbooks/setup-cloudflare-dns.yml --ask-vault-pass
For automated runs or to avoid typing the password every time, store it in a file and point ansible.cfg at it:
echo "your-vault-password" > ~/.ansible_vault_pass
chmod 600 ~/.ansible_vault_pass
# ansible.cfg
[defaults]
vault_password_file = ~/.ansible_vault_pass
Add ~/.ansible_vault_pass to your .gitignore. It stays on your machine; the encrypted vault files travel with your repo.
Encrypting Individual Strings
Sometimes you don't want a whole separate file - you just need to encrypt a single value inline. encrypt_string handles this:
ansible-vault encrypt_string 'your-token-here' --name 'cloudflare_api_token'
Output:
cloudflare_api_token: !vault |
$ANSIBLE_VAULT;1.1;AES256
62336138366562613266643065306630...
Paste that directly into any vars file. The !vault tag tells Ansible to decrypt it at runtime.
What You Have Now
group_vars/
├── all.yml ← non-sensitive defaults, committed in plain text
└── vault.yml ← encrypted, committed safely
roles/
├── cloudflare-dns/vars/
│ ├── main.yml ← references {{ vault_cloudflare_api_token }}
│ └── vault.yml ← encrypted API token
└── cloudflare-tunnel/vars/
├── main.yml
└── vault.yml ← encrypted tunnel credentials
Your repo is now safe to push. Secrets are encrypted at rest, decrypted at runtime, and never exposed in plain text.
What's Next
Part 5 puts all of this together with real services - deploying Docker containers using Ansible, managing compose files, and getting applications running across the homelab.