Skip to main content
RunBook Academy

AnsibleXXI · Secrets ManagementSecrets management

Vault IDs across environments

Advanced⏱ ~24 minansible-core

What you'll learn

  • Encrypt and decrypt with labelled vault IDs from prompt, file and client-script sources
  • State exactly what a vault ID label does and does not enforce by default
  • Configure DEFAULT_VAULT_ID_MATCH so that a label mismatch is refused rather than ignored
  • Design a vault ID scheme where the development password cannot open production material

Prerequisites

Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11

Not yet marked complete on this device.

One vault password for the whole estate means anyone who can run a development playbook can decrypt production credentials. Vault IDs exist to break that: separate passwords, separate labels, separate reach.

They work — but not in the way the name suggests, and the gap between the two is worth measuring rather than assuming.

The syntax

A vault ID is label@source:

Read-only / Safethree sources, one label
# Prompt for the password interactively
ansible-vault view --vault-id prod@prompt group_vars/prod/vault.yml

# Read it from a file
ansible-vault view --vault-id prod@~/.vault/prod-pass group_vars/prod/vault.yml

# Run an executable and use its stdout - the client-script form
ansible-vault view --vault-id prod@~/.vault/prod-client.py group_vars/prod/vault.yml

The label is recorded in the file header when you encrypt:

Read-only / Safethe label lands in the header
$ ansible-vault encrypt --vault-id prod@.prod-pass prod-secrets.yml; head -1 prod-secrets.yml
$ANSIBLE_VAULT;1.2;AES256;prod

Multiple IDs can be offered at once, and DEFAULT_VAULT_IDENTITY_LIST makes that the default for every invocation:

Read-only / Safeoffering several, without repeating yourself
# ansible.cfg
[defaults]
# Equivalent to passing --vault-id for each. Tried in order.
vault_identity_list = dev@~/.vault/dev-pass, prod@~/.vault/prod-pass

# Which one is used when encrypting, since the list has more than one
vault_encrypt_identity = dev

--encrypt-vault-id overrides vault_encrypt_identity for a single command. Without either, offering more than one identity and running an encryption makes the choice ambiguous, so set it deliberately — encrypting production material under the development label because it happened to be first in the list is a mistake that is invisible until somebody reads the header.

What the label actually enforces

Here is the part that matters. Four executed runs against the same file, encrypted under the label prod.

Read-only / SafeA. only the dev password offered
$ ansible-vault view --vault-id dev@.dev-pass prod-secrets.yml; echo exit=$?
[ERROR]: Failed to view 'prod-secrets.yml': Decryption failed
(no vault secrets were found that could decrypt).
exit=1

Good. The dev password does not open the prod file — because it is the wrong password, not because it carries the wrong label.

Read-only / SafeB. both passwords offered
$ ansible-vault view --vault-id dev@.dev-pass --vault-id prod@.prod-pass prod-secrets.yml; echo exit=$?
db_host: db1.example.com
db_password: REPLACE_ME
api_token: REDACTED
exit=0

Also expected. You offered the right password; it worked.

Now the one that changes how you should think about this.

Read-only / SafeC. the PROD password, offered under the label 'dev'
$ ansible-vault view --vault-id dev@.mislabelled-pass prod-secrets.yml; echo exit=$?
db_host: db1.example.com
db_password: REPLACE_ME
api_token: REDACTED
exit=0

The setting that closes it:

Read-only / SafeD. the same command with vault_id_match on
$ ANSIBLE_VAULT_ID_MATCH=True ansible-vault view --vault-id dev@.mislabelled-pass prod-secrets.yml; echo exit=$?
[ERROR]: Failed to view 'prod-secrets.yml': Decryption failed
(no vault secrets were found that could decrypt).
exit=1

Same password, same file, refused — because the label does not match.

Read-only / Safethe setting and its default
$ ansible-config dump | grep VAULT_ID_MATCH
DEFAULT_VAULT_ID_MATCH(default) = False

The documented description, from ansible-config list:

If true, decrypting vaults with a vault id will only try the password from the matching vault-id.

Designing the scheme

Labels should name the key, because that is what the label selects.

Read-only / Safea scheme that maps to who may decrypt what
dev       -> anyone on the engineering team
staging   -> engineering team
prod      -> platform on-call rota only
payments  -> payments team only; prod does not open it
ca-2026   -> the signing material, two-person access

Two rules that keep it honest.

One label per group of people who may decrypt. If two labels always travel together — everyone with prod also has staging — the second one is documentation, not separation. That may be fine, but know which it is.

Production credentials must not be reachable from a developer workstation. This is the whole objective, and vault IDs alone do not achieve it. The password file for prod should not exist on a laptop. If it does, the label scheme is describing an intention that the filesystem contradicts.

Knowledge check

Knowledge check · 4 questions

  1. Q1. On ansible-core 2.21.3 with default settings, a file encrypted under the label prod is opened with --vault-id dev@file, where that file contains the production password. What happens?

  2. Q2. A team believes their vault ID scheme prevents the development password from opening production files. With default settings, what actually provides that separation?

  3. Q3. Which are correct about enabling vault_id_match on an existing repository? Select all that apply.

  4. Q4. A vault ID scheme is sufficient to keep production credentials off a developer workstation, since the developer is only given the development label.

Passing score: 75%. Answers are checked in this browser.