Skip to main content
RunBook Academy

AnsibleXXXVII · Environments and Repository ArchitectureEnvironments and repository layout

Secrets separated by environment

Advanced⏱ ~20 minansible-core

What you'll learn

  • Lay out encrypted material so each environment has its own vault identity and its own password
  • State what a vault id label does and does not restrict
  • Predict what a run with the wrong vault id does, and at what point it stops
  • Identify which controllers and CI jobs must not hold more than one environment password

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.

Part XXI covers what Ansible Vault is, how to encrypt with it, how to rekey, and what the vault id labels do and do not enforce. This lesson assumes all of that and asks one structural question:

Can a developer with development credentials decrypt production secrets, even by accident?

The answer you want is no, and getting there is less about ansible-vault options than about which password file is on which machine.

The layout

Encrypted material lives inside the environment directory, beside the plaintext variables it belongs with:

inventories/
  production/
    group_vars/
      all/
        main.yml            # plaintext: ntp servers, log target
        vault.yml           # encrypted, vault id: prod
      webservers/
        20-limits.yml
        vault.yml           # encrypted, vault id: prod
  staging/
    group_vars/
      all/
        main.yml
        vault.yml           # encrypted, vault id: stage
  development/
    group_vars/
      all/
        main.yml
        vault.yml           # encrypted, vault id: dev

Three properties matter here and none of them is about encryption.

One vault id per environment, with a distinct password. Not one password with three labels. The label does not restrict anything; the password does.

A separate vault.yml rather than encrypting main.yml in place. An encrypted whole file cannot be diffed or reviewed, so putting the non-secret variables in it makes every ordinary change invisible to review. Split them and the plaintext file stays reviewable.

Encrypted files sit in the environment tree, not in a shared group_vars/ beside the playbook. A shared one applies to every environment — which means every environment needs the password to read it, which means there is no separation.

What the label actually does

This is the part people get wrong, and the consequence is a scheme that looks separated and is not.

A vault id label is recorded in the file header:

Read-only / Safethe header carries the label
$ head -1 inventories/production/group_vars/all/vault.yml
$ANSIBLE_VAULT;1.2;AES256;prod

That label tells Ansible which supplied password to try first. By default it does not restrict which passwords may be tried at all. Offer the production password under the label dev and the file opens anyway:

Read-only / Safethe label does not have to be true
$ ansible-vault view --vault-id dev@~/.vault/prod inventories/production/group_vars/all/vault.yml
db_password: REDACTED-prod

Part XXI’s vault-id lesson covers DEFAULT_VAULT_ID_MATCH, the setting that makes the label binding, and it is worth turning on. But do not mistake it for the control. The reason a developer cannot read production secrets is not that a label mismatched — it is that the production password was never on their machine.

What a wrong vault id does to a run

This is the good news, and it is worth knowing precisely, because it means the mistake fails in the safest possible way.

Group variables are decrypted while the play is being set up, before any host is contacted. A wrong password therefore stops the run before a single connection is attempted:

Configuration changeproduction inventory, development vault id
$ ansible-playbook -i inventories/production --vault-id dev@~/.vault/dev playbooks/webservers.yml
PLAY [Configure web tier] ******************************************************
[ERROR]: Decryption failed (no vault secrets were found that could decrypt).

Exit status 4. Zero tasks attempted, zero hosts touched, no recap.

That is the behaviour you want from a wrong-credential mistake, and it is worth stating in the runbook: if you see this error against production, you supplied the wrong vault id — nothing has been changed.

The same property applies to inventory inspection, which is easy to forget when you are only trying to look at something:

Read-only / Safeyou cannot even list the inventory without the password
$ ansible-inventory -i inventories/production --graph
[ERROR]: Attempting to decrypt but no vault secrets found.

Knowledge check

Knowledge check · 4 questions

  1. Q1. What actually prevents a developer with the dev vault password from decrypting production secrets?

  2. Q2. A run against the production inventory is given only the development vault id. Which statements are true of what happens next, on 2.21.3? Select all that apply.

  3. Q3. Adding ansible-playbook --syntax-check against the production inventory to CI will catch a misconfigured vault id before deploy time.

  4. Q4. Which of these is the most dangerous entry in a vault password distribution table?

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