Ansible

Ansible Vault: Encrypting Secrets and Passwords

Sooner or later, every Ansible project hits the same wall: database passwords sitting in plain YAML, API keys checked into Git, and that one group_vars/all.yml file that makes the security team lose sleep. Ansible Vault solves this by encrypting sensitive data at rest while keeping your playbooks fully functional. Vault-encrypted files and strings decrypt automatically at runtime, and the secrets never appear in logs or console output.

Original content from computingforgeeks.com - post 165311

This guide walks through every Vault operation you’ll actually use in production, from encrypting entire files to inline string encryption, multiple vault IDs for environment separation, and CI/CD integration. We’ll work through real examples on Rocky Linux 10.1 and Ubuntu 24.04 with ansible-core 2.16.14. If you’re already comfortable with writing playbooks, you’ll pick this up fast. For a quick command reference, the Ansible Vault cheat sheet covers the essentials.

Verified working: April 2026 on Rocky Linux 10.1 and Ubuntu 24.04 LTS, ansible-core 2.16.14

Prerequisites

Before starting, confirm you have the following in place:

  • Ansible installed on your control node. See Install and Configure Ansible on Linux if you need to set it up
  • Tested on: ansible-core 2.16.14, Rocky Linux 10.1, Ubuntu 24.04 LTS
  • Basic familiarity with Ansible playbooks and YAML syntax
  • A text editor set via $EDITOR (vim, nano, etc.)

Encrypt an Entire File

The most common use case is encrypting a variables file that contains secrets. Start by creating a YAML file with the sensitive values.

Create the secrets file:

mkdir -p vars
cat > vars/db_secrets.yml << 'EOF'
---
db_host: 10.0.1.50
db_port: 5432
db_name: appdb
db_user: appuser
db_password: Pr0duction$ecret!
db_root_password: R00t@dm1n2026
api_key: sk-proj-abc123def456ghi789
EOF

Before encrypting, you need a vault password. Store it in a file so you don’t have to type it every time:

openssl rand -base64 32 > .vault_pass
chmod 600 .vault_pass

Now encrypt the secrets file:

ansible-vault encrypt vars/db_secrets.yml --vault-password-file .vault_pass

Vault confirms the operation immediately:

Encryption successful

The file is now AES-256 encrypted. If you look at its contents, there’s nothing readable:

head -5 vars/db_secrets.yml

The output is pure ciphertext:

$ANSIBLE_VAULT;1.1;AES256
64323364373931386330646632356137343664396461343534643131313933663634343562666430
3064366233626233653466336637303365346165316361340a643732343133346632393631393335
63663032303330643934666637393631613330373633303133623337646132336435633832663230
3236346431336130620a633630396261663766343061353963336663373030306661363634646135

That $ANSIBLE_VAULT;1.1;AES256 header tells Ansible this file is vault-encrypted and which cipher was used. You can safely commit this to Git because the contents are meaningless without the vault password.

View Encrypted Files

To read the contents without permanently decrypting the file on disk, use vault view:

ansible-vault view vars/db_secrets.yml --vault-password-file .vault_pass

The decrypted content displays in your terminal but the file stays encrypted:

---
db_host: 10.0.1.50
db_port: 5432
db_name: appdb
db_user: appuser
db_password: Pr0duction$ecret!
db_root_password: R00t@dm1n2026
api_key: sk-proj-abc123def456ghi789

If you need to permanently decrypt a file (to stop using Vault for it), run ansible-vault decrypt vars/db_secrets.yml --vault-password-file .vault_pass. The file reverts to plain YAML.

Encrypt a Single String

Sometimes you don’t want to encrypt an entire file. Maybe your vars/main.yml has 20 variables and only 2 are sensitive. The encrypt_string subcommand encrypts a single value that you paste directly into a playbook or vars file.

ansible-vault encrypt_string "SuperSecretAPI123" --name "api_token" --vault-password-file .vault_pass

Vault outputs a block you can drop straight into YAML:

api_token: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          35343263323635363030366436366164326633356164373535616630326165333234306537616231
          3337336462316239613331633930353035306632306431370a396537373566623734623565386665
          61663666373639626434613362393337643737326335666261363030653465323161303338386630
          3239656666623162650a636463323733636531623136303530336430643963333761333637633966
          31393130306463653962613137313537383939313239376365663838633061383166

Copy that entire block into your vars file alongside the non-sensitive variables. Ansible decrypts it at runtime just like a full-file encrypted var. The !vault tag tells the YAML parser to hand this value off to Vault for decryption.

Use encrypt_string when only a few values in a file are sensitive and you want to keep the rest readable. Use full-file encryption when most of the file is secrets, or when the file structure itself reveals something about your infrastructure.

Use Vault Secrets in Playbooks

Encryption is only useful if your playbooks can consume the secrets at runtime. Here’s a playbook that loads the encrypted vars file and deploys an application config with those credentials. The structure follows standard Ansible role patterns.

Create the playbook:

cat > deploy_app.yml << 'EOF'
---
- name: Deploy app with vault-encrypted secrets
  hosts: managed-rocky
  become: true
  vars_files:
    - vars/db_secrets.yml

  tasks:
    - name: Show database connection info (safe fields only)
      ansible.builtin.debug:
        msg: "Connecting to {{ db_host }}:{{ db_port }}/{{ db_name }} as {{ db_user }}"

    - name: Create application config with secrets
      ansible.builtin.copy:
        dest: /opt/app/config.ini
        mode: '0600'
        content: |
          [database]
          host={{ db_host }}
          port={{ db_port }}
          name={{ db_name }}
          user={{ db_user }}
          password={{ db_password }}

    - name: Verify config was created
      ansible.builtin.stat:
        path: /opt/app/config.ini
      register: config_stat

    - name: Show config file permissions
      ansible.builtin.debug:
        msg: "Config file: mode={{ config_stat.stat.mode }}, size={{ config_stat.stat.size }} bytes"
EOF

Running with –ask-vault-pass

For interactive use, pass --ask-vault-pass and Ansible prompts you for the password:

ansible-playbook deploy_app.yml --ask-vault-pass

Running with –vault-password-file

For automation and CI/CD, point to the password file instead:

ansible-playbook deploy_app.yml --vault-password-file .vault_pass

The play runs and the secrets are injected into the config file without ever appearing in the output:

PLAY [Deploy app with vault-encrypted secrets] *********************************

TASK [Gathering Facts] *********************************************************
ok: [managed-rocky]

TASK [Show database connection info (safe fields only)] ************************
ok: [managed-rocky] => {
    "msg": "Connecting to 10.0.1.50:5432/appdb as appuser"
}

TASK [Create application config with secrets] **********************************
changed: [managed-rocky]

TASK [Verify config was created] ***********************************************
ok: [managed-rocky]

TASK [Show config file permissions] ********************************************
ok: [managed-rocky] => {
    "msg": "Config file: mode=0600, size=121 bytes"
}

PLAY RECAP *********************************************************************
managed-rocky              : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Notice that the debug task only prints db_host, db_port, db_name, and db_user. The actual password never shows up. Even if you run the playbook with -v or -vvv for verbose output, Ansible masks vault-encrypted values. This was verified by grepping the verbose output for db_password, which returned zero matches.

Edit Encrypted Files

When you need to update a secret (rotate a password, add a new key), use vault edit:

ansible-vault edit vars/db_secrets.yml --vault-password-file .vault_pass

This decrypts the file in memory, opens it in your $EDITOR, and re-encrypts it when you save and quit. The plaintext never touches disk. If you close the editor without saving, the file stays unchanged.

Set your preferred editor before running the command if it’s not already configured:

export EDITOR=vim

The vault edit workflow is the safest way to modify encrypted files because the decrypted content exists only in memory during the editing session.

Rekey: Change the Vault Password

Password rotation is a fact of life. A team member leaves, a password gets accidentally shared, or your security policy mandates periodic rotation. The rekey command re-encrypts files with a new password without requiring a manual decrypt-edit-encrypt cycle.

Generate a new vault password first:

openssl rand -base64 32 > .vault_pass_new

Rekey the encrypted file:

ansible-vault rekey vars/db_secrets.yml --vault-password-file .vault_pass --new-vault-password-file .vault_pass_new

Vault confirms the password change:

Rekey successful

After rekeying, replace your old password file and update any automation that references it:

mv .vault_pass_new .vault_pass

You can rekey multiple files in one command by listing them all. In a large project, use find . -name "*.yml" -exec grep -l "ANSIBLE_VAULT" {} + to locate every encrypted file before running rekey against them.

Multiple Vault IDs: Separating Dev and Prod

In production environments, you don’t want the same password protecting development and production secrets. If a developer’s laptop is compromised, your prod secrets shouldn’t be at risk. Vault IDs solve this by letting you tag encrypted files with a label and use separate passwords for each.

Create separate password files for each environment:

openssl rand -base64 32 > .vault_pass_dev
openssl rand -base64 32 > .vault_pass_prod
chmod 600 .vault_pass_dev .vault_pass_prod

Encrypt files with their respective vault IDs:

ansible-vault encrypt vars/dev_secrets.yml --vault-id [email protected]_pass_dev
ansible-vault encrypt vars/prod_secrets.yml --vault-id [email protected]_pass_prod

The encrypted file headers now include the vault ID label. Check the difference:

head -1 vars/dev_secrets.yml
head -1 vars/prod_secrets.yml

The headers show which vault ID was used for each file:

$ANSIBLE_VAULT;1.2;AES256;dev
$ANSIBLE_VAULT;1.2;AES256;prod

Note the format version bumps to 1.2 when using vault IDs. This is automatic.

When running a playbook that needs both dev and prod secrets, pass both vault IDs:

ansible-playbook site.yml --vault-id [email protected]_pass_dev --vault-id [email protected]_pass_prod

Ansible matches each encrypted file to the correct password based on the vault ID in its header. Give developers only the dev password and restrict the prod password to your deployment pipeline.

Vault in CI/CD Pipelines

Interactive password prompts don’t work in CI/CD. You have two clean options for feeding the vault password to automated runs.

Option 1: Password File

Store the vault password as a CI/CD secret (GitHub Actions secret, GitLab CI variable, Jenkins credential) and write it to a temporary file at pipeline runtime:

echo "$VAULT_PASSWORD" > /tmp/.vault_pass
chmod 600 /tmp/.vault_pass
ansible-playbook deploy.yml --vault-password-file /tmp/.vault_pass
rm -f /tmp/.vault_pass

Option 2: Environment Variable

Set ANSIBLE_VAULT_PASSWORD_FILE in your pipeline environment. Ansible checks this variable automatically, so you don’t need the --vault-password-file flag at all:

export ANSIBLE_VAULT_PASSWORD_FILE=/tmp/.vault_pass
ansible-playbook deploy.yml

You can also set this permanently in ansible.cfg for a project:

[defaults]
vault_password_file = .vault_pass

With this in place, every ansible-playbook and ansible-vault command in the project directory automatically uses that password file. One less flag to remember. This is covered in more detail in the Ansible automation guide.

Never Commit the Password File

Add vault password files to .gitignore immediately:

echo ".vault_pass*" >> .gitignore

Committing the password file defeats the entire purpose of encryption. The encrypted files are safe to commit. The password is not.

Best Practices

After working with Vault across dozens of projects, a few patterns consistently prevent problems. This table covers what works and what causes headaches, based on the official Ansible Vault documentation.

DoDon’t
Use .vault_pass with mode 0600Store vault passwords in shared docs or chat
Add .vault_pass* to .gitignoreCommit vault password files to the repository
Use vault IDs to separate dev/staging/prodUse one password for all environments
Rekey after team members leaveAssume old passwords are still safe
Encrypt only what’s sensitiveEncrypt entire playbooks (makes diffs useless)
Use encrypt_string for individual valuesEncrypt files with only 1 secret and 20 non-sensitive vars
Name encrypted files clearly (*_secrets.yml)Mix encrypted and plain vars in the same file
Test decryption in CI before deployingDiscover wrong password during a production deploy

When to Use Vault vs External Secret Managers

Ansible Vault is excellent for small to mid-sized teams where secrets are managed alongside playbooks. It requires zero additional infrastructure. For larger organizations or environments with strict compliance requirements, consider HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault. These tools provide centralized secret storage, audit trails, dynamic credentials, and lease-based access that Ansible Vault doesn’t offer. You can integrate them with Ansible using lookup plugins while still using Ansible Vault for bootstrap secrets like the API tokens needed to reach those external systems.

Troubleshooting

Error: “Decryption failed (no vault secrets were found that could decrypt)”

This is the most common vault error. It means the password you provided doesn’t match the one used to encrypt the file. Check which vault ID the file expects by looking at the header:

head -1 vars/db_secrets.yml

If the header shows $ANSIBLE_VAULT;1.2;AES256;prod, you need to pass the prod vault ID with the correct password. A header showing $ANSIBLE_VAULT;1.1;AES256 (no ID label) means it was encrypted without a vault ID, so any vault password argument will be tried against it.

Error: “input is already encrypted”

This happens when you accidentally run ansible-vault encrypt on a file that’s already encrypted. Vault refuses to double-encrypt because it would create a mess. If you need to re-encrypt with a different password, use rekey instead of decrypt-then-encrypt.

Error: “ERROR! Attempting to decrypt but no vault secrets found”

You ran ansible-playbook against a playbook that references an encrypted file, but forgot to pass --ask-vault-pass or --vault-password-file. Either add the flag or set vault_password_file in your ansible.cfg. The Ansible cheat sheet has a quick reference for common flags.

Quick Reference

Here’s every vault command covered in this guide, consolidated for fast lookup. For more Ansible operations, the Ansible cheat sheet covers the broader toolset.

OperationCommand
Encrypt a fileansible-vault encrypt file.yml
Decrypt a fileansible-vault decrypt file.yml
View encrypted fileansible-vault view file.yml
Edit encrypted fileansible-vault edit file.yml
Encrypt a stringansible-vault encrypt_string "value" --name "key"
Rekey (change password)ansible-vault rekey file.yml
Run playbook with vaultansible-playbook site.yml --ask-vault-pass
Run with password fileansible-playbook site.yml --vault-password-file .vault_pass
Run with vault IDansible-playbook site.yml --vault-id [email protected]_pass_prod

Vault integrates cleanly into existing Ansible workflows. Once your secrets are encrypted, the only operational change is passing a password or password file when running playbooks. For teams managing infrastructure with Ansible alongside tools like Terraform, Vault keeps the secrets layer simple without adding another system to maintain. If you’re building out a broader automation setup, the Ansible automation guide ties these concepts together with roles, inventories, and certificate management.

Related Articles

Security Configuring OpenSSH Server on Windows Server 2019 Kubernetes Use Let’s Encrypt SSL Certificates on OpenShift 4.x Ingress / Routes pfSense How To Join pfSense to Tailscale / Headscale Mesh Security Best CompTIA Security+ (SY0-701) Books for 2026

Leave a Comment

Press ESC to close