Ansible Lesson 26 – Ansible Galaxy | Dataplexa
Section III · Lesson 26

Introduction to Ansible Vault

In this lesson

What Vault solves Encrypting files Encrypting strings Vault passwords Using Vault in playbooks

Ansible Vault is the built-in encryption system that allows sensitive data — passwords, API keys, SSL private keys, database credentials — to be stored safely in version-controlled files alongside the rest of your automation. Without Vault, every engineer who needs to run a playbook must either store secrets in plaintext files (a security incident waiting to happen) or supply them manually at the command line (which breaks automation). Vault solves both problems: secrets are encrypted with AES-256, committed to Git alongside the playbooks that use them, and decrypted automatically at playbook runtime using a password that lives only in secure, non-committed storage.

The Problem Vault Solves

Before Ansible Vault, teams commonly stored credentials in one of three dangerous ways. Each one has caused real production incidents.

Plaintext in group_vars

Credentials visible to everyone with repo access. One leaked token or mis-scoped repository permission exposes every secret in the project.

Excluded from version control

Secrets on one engineer's laptop. Anyone else who needs to run the playbook cannot. Engineer leaves — secrets leave with them.

Passed with -e at runtime

Credentials visible in shell history and process lists. Breaks CI/CD pipelines that need to run unattended.

🔒

Ansible Vault: the right answer

Secrets are AES-256 encrypted, committed to Git alongside the playbooks that use them, and decrypted automatically at runtime. The vault password is the only secret that must be kept out of version control — and it can be stored in a secrets manager, CI/CD environment variable, or a .vault_pass file listed in .gitignore.

The Safe Deposit Box Analogy

Ansible Vault is like a bank's safe deposit box inside your Git repository. The box (encrypted vault file) is kept in the bank (the repository) where everyone can see it exists — but only someone with the right key (vault password) can open it and read the contents. You can commit the box to version control, share it with your whole team, and even make it public — because without the key, the contents are meaningless ciphertext. The key is the only thing you protect carefully.

Encrypting and Editing Files

The most common Vault workflow is to create a dedicated secrets file, encrypt it, and reference it in your playbook via vars_files. All ansible-vault subcommands follow the same pattern — they operate on files or strings and use the vault password to encrypt/decrypt.

# Create and encrypt a new file in one step
ansible-vault create vars/secrets.yml

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

# View the decrypted contents without opening an editor
ansible-vault view vars/secrets.yml

# Edit the file — decrypts to a temp file, opens your $EDITOR, re-encrypts on save
ansible-vault edit vars/secrets.yml

# Decrypt permanently to plaintext (be careful — do not commit the result)
ansible-vault decrypt vars/secrets.yml

# Re-encrypt with a new password
ansible-vault rekey vars/secrets.yml
# What vars/secrets.yml looks like after encryption:
$ANSIBLE_VAULT;1.1;AES256
38643865363532643137643932336439373633623430343766303839366135663066633
66561663737346437326262376261656530376666323230656530383733353564636564
3365353738383733333466666630353164636536336333330a643866633634323832376
63639366231376566303231646430376232363061613339303061343035363232633061
38643334303230373264636133633162353738363838313032643464393734353135373
3134...

# The file is unreadable without the vault password.
# It is safe to commit to Git.

What just happened?

The AES-256 encrypted file starts with $ANSIBLE_VAULT;1.1;AES256 — a header that tells Ansible this is a vault-encrypted file. Every subsequent line is hex-encoded ciphertext. Ansible automatically detects this header at runtime and decrypts the file before loading the variables — no manual decryption step required.

Contents of vars/secrets.yml before encryption

---
# vars/secrets.yml — ENCRYPT THIS FILE before committing
# ansible-vault encrypt vars/secrets.yml

vault_db_password: "s3cr3tP@ssw0rd!"
vault_api_key: "sk-prod-abc123xyz789"
vault_ssl_private_key: |
  -----BEGIN OPENSSH PRIVATE KEY-----
  b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAE...
  -----END OPENSSH PRIVATE KEY-----
vault_smtp_password: "mailpass456"

Encrypting Individual Strings

Sometimes you do not want a completely separate secrets file — you want to embed an encrypted value inline within an otherwise readable variable file. The ansible-vault encrypt_string command produces an encrypted string that can be pasted directly into any YAML file. The rest of the file remains human-readable.

# Encrypt a string value — prompts for vault password then the string to encrypt
ansible-vault encrypt_string 's3cr3tP@ssw0rd!' --name 'db_password'

# Non-interactive — pipe the value in (useful in scripts)
echo -n 's3cr3tP@ssw0rd!' | ansible-vault encrypt_string --stdin-name 'db_password'
db_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          62313030386234336665653736653539643735363538643538616539363534396662303865613164
          6566363336346165303038663432386661393031636338310a363533373830373264363531373761
          36363630363535333934363630636337393164336432376633323933313731623163396637353036
          3736353338326564640a626164323165353965363665326163363339343437376630623866343765
          6265

What just happened?

The output is a YAML snippet ready to paste directly into a variable file. The !vault tag tells Ansible this value is vault-encrypted. Copy this entire block and paste it into your group_vars/all.yml or any other variable file — the surrounding variables remain readable while only the sensitive value is encrypted.

Using inline encrypted strings in a variable file

# group_vars/databases.yml — mostly readable, one encrypted value
---
postgresql_version: "15"
postgresql_port: 5432
postgresql_max_connections: 100

# This value is encrypted with ansible-vault encrypt_string
# The rest of the file is readable in plain text
postgresql_app_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          62313030386234336665653736653539643735363538643538616539363534396662303865613164
          6566363336346165303038663432386661393031636338310a363533373830373264363531373761
          36363630363535333934363630636337393164336432376633323933313731623163396637353036
          3736353338326564640a626164323165353965363665326163363339343437376630623866343765
          6265

Managing the Vault Password

The vault password is the one secret that must never be committed to version control. It must be kept secure but also accessible enough for automated pipelines to use it unattended. There are three standard approaches — choose based on your security requirements and team setup.

Method 1

Password file

Store the password in .vault_pass in the project root. Add to .gitignore. Set vault_password_file = .vault_pass in ansible.cfg. Simple and effective for small teams.

Method 2

Environment variable

Set ANSIBLE_VAULT_PASSWORD_FILE to a path, or use a vault password script that reads from an environment variable. Standard for CI/CD — the password is injected by the pipeline without touching the filesystem.

Method 3

Vault password script

An executable script that retrieves the password from a secrets manager (HashiCorp Vault, AWS Secrets Manager, 1Password CLI) and prints it to stdout. The most secure option — no password ever touches the filesystem.

# ansible.cfg — configure vault password source

[defaults]
# Method 1 — password file (simple, add .vault_pass to .gitignore)
vault_password_file = .vault_pass

# Method 2 — environment variable (no config needed, set in shell or CI)
# export ANSIBLE_VAULT_PASSWORD_FILE=/path/to/vault_pass

# Method 3 — password script (reads from secrets manager)
# vault_password_file = ./scripts/get_vault_password.py
# Create the vault password file
echo "my-strong-vault-password" > .vault_pass
chmod 600 .vault_pass          # restrict to owner only

# Verify .vault_pass is in .gitignore — CRITICAL
grep ".vault_pass" .gitignore  # should return .vault_pass

# Add it if missing
echo ".vault_pass" >> .gitignore

Using Vault in Playbooks

Once vault is configured in ansible.cfg, using encrypted variables in a playbook requires no special syntax — reference them exactly like any other variable. Ansible detects the $ANSIBLE_VAULT header or !vault tag and decrypts automatically at runtime.

---
- name: Configure database server
  hosts: databases
  become: true

  vars_files:
    - vars/app.yml          # plaintext — readable in Git
    - vars/secrets.yml      # vault-encrypted — safe in Git

  tasks:
    # vault_db_password comes from the encrypted secrets.yml
    # Reference it exactly like any other variable — no special syntax
    - name: Create application database user
      community.postgresql.postgresql_user:
        name: "{{ app_db_user }}"
        password: "{{ vault_db_password }}"   # decrypted automatically
        state: present
      become_user: postgres
      no_log: true   # prevent password from appearing in output

    - name: Configure application with API key
      ansible.builtin.template:
        src: app.conf.j2
        dest: /etc/app/config.conf
      # In app.conf.j2: api_key = {{ vault_api_key }}
# Running the playbook — vault is decrypted automatically using .vault_pass
ansible-playbook site.yml -i inventory/production/

# If vault_password_file is not in ansible.cfg, prompt interactively
ansible-playbook site.yml --ask-vault-pass

# Specify vault password file at the command line
ansible-playbook site.yml --vault-password-file .vault_pass

Vault IDs — Multiple Passwords

Large projects often need different vault passwords for different environments or secret categories — production credentials should not be decryptable with the same password used for staging. Vault IDs allow multiple passwords to coexist in one project.

Without Vault IDs
One vault password for everything
Staging and production secrets encrypted with the same key
Anyone who can decrypt staging can also decrypt production
With Vault IDs
Separate password per environment or category
Production secrets only decryptable with the production vault password
Principle of least privilege applied to secrets
# Encrypt with a specific vault ID
ansible-vault encrypt vars/prod_secrets.yml --vault-id prod@.vault_pass_prod
ansible-vault encrypt vars/staging_secrets.yml --vault-id staging@.vault_pass_staging

# Encrypt a string with a vault ID
ansible-vault encrypt_string 'prod-password' --name 'db_pass' \
  --vault-id prod@.vault_pass_prod

# Run playbook supplying both vault passwords
ansible-playbook site.yml \
  --vault-id prod@.vault_pass_prod \
  --vault-id staging@.vault_pass_staging

Vault Command Reference

ansible-vault subcommands

create Create a new encrypted file and open it in $EDITOR. Save and close to encrypt automatically.
encrypt Encrypt an existing plaintext file in place. The original is replaced with ciphertext. Safe to run multiple times — detects already-encrypted files.
decrypt Decrypt a file to plaintext permanently. Use with caution — the result should not be committed. Useful for inspecting content or rotating to a new password.
view Display the decrypted contents without permanently decrypting the file. Safe to use at any time — the file on disk remains encrypted.
edit Decrypt to a temporary file, open in $EDITOR, re-encrypt on save. The everyday command for updating secrets — the file is never decrypted on disk beyond the temporary editing session.
rekey Change the vault password for an encrypted file. Prompts for the old password then the new one. Use when rotating the vault password across the project.
encrypt_string Encrypt a single string value and output a YAML snippet suitable for inline use in variable files. The surrounding file remains readable plaintext.

Always Verify .vault_pass Is in .gitignore Before the First Commit

The most catastrophic Vault mistake is committing the vault password file to version control — which exposes every secret in every encrypted file in the repository, present and historical. Before your first commit in any Vault-enabled project, run git status and confirm .vault_pass is listed as untracked but not staged. Then verify .gitignore contains the entry. Do this check every time you add a new vault password file to a project. A single accidental commit means rotating every secret in the repository — a painful, time-consuming process that is completely avoidable.

Key Takeaways

Ansible Vault uses AES-256 encryption — encrypted files are safe to commit to any Git repository, including public ones. The only secret to protect is the vault password itself.
Use encrypt_string for inline secrets — encrypt a single value and embed it in an otherwise readable variable file. The file stays navigable; only the sensitive value is opaque.
Set vault_password_file in ansible.cfg — this makes Vault fully transparent at runtime. Playbooks decrypt secrets automatically with no extra flags needed.
Name vault variables with a vault_ prefixvault_db_password rather than db_password. This makes it immediately clear which variables are encrypted and where their values come from when reading a playbook.
Use Vault IDs for separate staging and production passwords — preventing anyone with staging access from decrypting production secrets is a basic security boundary worth enforcing from day one.

Teacher's Note

Take any variable file in your project that contains a plaintext password, encrypt it with ansible-vault encrypt, run the playbook, and confirm it still works. Then use ansible-vault edit to change one value and run it again. This two-step exercise makes Vault part of your muscle memory rather than a special procedure you look up when you need it.

Practice Questions

1. Which ansible-vault subcommand encrypts a single string value and outputs a YAML snippet you can paste inline into any variable file?



2. Which subcommand decrypts a vault file to a temporary location, opens it in your editor, and automatically re-encrypts it when you save and close — without ever writing plaintext to disk permanently?



3. In which project file must you list .vault_pass to ensure the vault password is never accidentally committed to version control?



Quiz

1. A variable db_password in group_vars/databases.yml is encrypted with !vault |. How do you reference it in a task?


2. A team member who knew the vault password has left the company. What is the correct procedure?


3. A company wants developers to be able to run playbooks against staging but not be able to decrypt production secrets. What Vault feature enables this?


Up Next · Lesson 27

Advanced Vault Techniques

Go deeper with Vault — partial encryption strategies, vault password scripts that integrate with HashiCorp Vault and AWS Secrets Manager, and CI/CD pipeline patterns for unattended secret management.