Multiple Vaults with Vault IDs

Scenario: One passphrase unlocks development, staging and production. A contractor needs to run playbooks against development, and giving them the key gives them production too.

New words, in plain English

A single vault passphrase means a single failure domain. Anyone who can run development automation can decrypt production secrets, and rotating the key means coordinating everyone at once.

Vault IDs fix this by labelling encrypted files. ansible-vault encrypt --vault-id prod@prompt file.yml stamps the file with the prod label. At run time, Ansible tries the passphrase matching that label. Supply several with repeated --vault-id flags, or list them in ansible.cfg under vault_identity_list, and each file is decrypted by its own key.

The practical arrangement is one vault ID per environment. Developers hold dev. CI for staging holds dev and staging. Only the production deployment pipeline holds prod. A leaked development key exposes development, and rotating it affects only development.

This composes naturally with the vars/vault split: group_vars/production/vault.yml encrypted with the prod ID, group_vars/staging/vault.yml with staging, and readable vars.yml files alongside both.

Analogy: One master key for every door in the building is convenient until it is lost. Separate keys per floor mean a lost key costs you one floor and one lock change, not the whole building at once.

A worked example

# Encrypt with a labelled vault ID
ansible-vault encrypt --vault-id prod@prompt group_vars/production/vault.yml
ansible-vault encrypt --vault-id dev@prompt  group_vars/development/vault.yml

# Run with several labelled keys available
ansible-playbook site.yml \
  --vault-id dev@~/.vault_dev \
  --vault-id prod@~/.vault_prod

# ansible.cfg - the same, without flags
[defaults]
vault_identity_list = dev@~/.vault_dev, staging@~/.vault_staging

# A developer simply does not have ~/.vault_prod,
# so production secrets stay unreadable to them.

# The passphrase can come from a real secret manager:
#   vault_identity_list = prod@~/bin/fetch-prod-vault-key.sh
# (an executable - Ansible uses its stdout)

The @prompt form is useful for interactive work and for the initial encryption, but automation needs a file or an executable. Pointing a vault ID at a script that retrieves the passphrase from a real secret manager is the best of both: nothing is stored on disk, the retrieval is audited by the secret manager, and access can be revoked centrally without touching the repository.

One operational note: if Ansible cannot match a vault ID it falls back to trying every available passphrase, which is convenient and slightly blunts the isolation. Where separation genuinely matters, keep the keys physically apart - on different machines and in different pipelines - rather than relying on labelling alone.

Tip: One vault ID per environment, and the production key only on the production pipeline. A leaked development key should never be a production incident.
Goal: Put this to work in the ansible-vault-secrets lab. Open /labs/ansible, pick ansible-vault-secrets, and fix the real broken project - Ansible really does SSH into four managed hosts and converge them.