Learn Puppet - Hiera (Hierarchical Data)
Episode 7 of 23

Learn Puppet - Hiera (Hierarchical Data)

In this episode we separate data from code with Hiera 5: layered data hierarchies, hiera.yaml, the lookup function, YAML and JSON data in modules and environments, and encrypting secrets with hiera-eyaml.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 6 you learned how to read node data through facts and turn it into decisions inside manifests. But writing if and case for every possible value will fill your code with business logic. In episode 7 we move data out of the code with Hiera 5, Puppet's built-in hierarchical data system.

With Hiera, configuration is stored as pure data — YAML or JSON — looked up through a hierarchy. Manifest code just fetches data via lookup, and the same value can handle all nodes simply by changing its position in the hierarchy.

Hiera 5 Concepts

Hiera solves one problem: where a value comes from, and who has priority. Each datum is defined once in a flat file, then the hierarchy determines the order of priority. The more specific a level, the higher its priority.

hiera.yaml at the environment level
version: 5
defaults:
  datadir: data
  data_hash: yaml_data
hierarchy:
  - name: "Per-node data"
    path: "nodes/%{trusted.certname}.yaml"
  - name: "Per-OS family"
    path: "osfamily/%{facts.os.family}.yaml"
  - name: "Per-OS"
    path: "os/%{facts.os.name}.yaml"
  - name: "Common"
    path: "common.yaml"

The hierarchy is read from the top: Hiera tries nodes/<certname>.yaml first. If the key isn't found there, it moves down to osfamily, then os, and finally common.yaml.

Important

The location of hiera.yaml determines its scope. In /etc/puppetlabs/puppet/hiera.yaml the hierarchy applies to the whole environment; inside a module's data/ folder, the hierarchy only applies to that module. Environment configuration takes precedence over modules.

Structuring Data at Three Levels

The most common data levels are common, osfamily, and hostname. Together they represent a healthy strategy: common values for everyone, per-OS-family adjustments, and per-node exceptions.

data/common.yaml
ntp_servers:
  - 0.pool.ntp.org
  - 1.pool.ntp.org
dns_servers:
  - 8.8.8.8
  - 1.1.1.1
timezone: UTC
data/osfamily/RedHat.yaml
ssh_packages:
  - openssh-server
web_server_pkg: httpd
data/nodes/web01.example.com.yaml
timezone: Asia/Jakarta
app_port: 8080

Notice the pattern: common.yaml holds the default values, the per-OS-family file holds customizations that apply to many nodes, and the per-node file holds exceptions. Node web01.example.com gets timezone: Asia/Jakarta from the node level, not UTC from common.

Tip

The per-node file name must exactly match the certname — letter case matters too. If the certname is WEB01.EXAMPLE.COM, the Hiera file must also be WEB01.EXAMPLE.COM.yaml.

The lookup Function

Inside manifests, Hiera data is fetched with the lookup function. The simplest form:

Basic lookup
$ntp = lookup('ntp_servers')
$tz  = lookup('timezone')

lookup can be constrained by data type and given a default so it doesn't error when the key is missing:

lookup with type and default
$port = lookup('app_port', Integer, 'first', 80)
$dns  = lookup('dns_servers', Array[String], 'unique')
$rules = lookup('firewall_rules', Hash, 'deep')

The second parameter constrains the result type, the third is the merge strategy, and the fourth is the default value. The merge strategy determines how values from multiple levels are combined:

StrategyBehavior
firstUse the value from the highest level that has the key
uniqueMerge all array values from all levels, removing duplicates
hashMerge hashes across levels, higher-level keys win
deepLike hash, but nested keys are merged too

For example, lookup('ntp_servers', Array[String], 'unique') will merge the NTP server lists from common.yaml and other levels without duplicates.

YAML and JSON Data in Modules

Modules can also carry their own data through a data/ directory. This is useful for a module's default values, while the environment still has the right to override them:

Module structure with data
profile::web/
  data/
    common.yaml
  hiera.yaml
  manifests/
  templates/
profile/web/data/common.yaml
profile::web::worker_processes: 4
profile::web::listen_port: 80

Notice the key names: profile::web::worker_processes follows the fully qualified name of the class parameter. Hiera then fills it in automatically as a class parameter when the class is included — this is the main bridge between data and code.

Note

To use automatic lookup for class parameters, the class must use automatic parameter binding — just include without class { ... }. Hiera data named name::module::parameter will be wired up by itself.

hiera-eyaml for Secrets

Secrets like passwords should never sit in plaintext in git. hiera-eyaml is a Hiera backend that stores encrypted values inside regular YAML files, while the private key is kept on the server.

Prepare the keys and encrypt a value:

Set up keys and encrypt a secret
sudo puppet module install puppetlabs-eyaml --modulepath /etc/puppetlabs/puppet/modules
eyaml encrypt --pkcs7-private-key=/etc/puppetlabs/eyaml/keys/private_key.pkcs7.pem \
  --pkcs7-public-key=/etc/puppetlabs/eyaml/keys/public_key.pkcs7.pem \
  --string "S3cr3t"

The output of the command above is an ENC[PKCS7,...] block ready to be placed in the data. Example result:

data/secrets.yaml with an encrypted value
db_password: ENC[PKCS7,MIIEvgYJKoZIhvcNAQcCoIIErzCCBKsCAQExAAEwDQYJKoZIhvcNAQEBBQAEggSg...]
db_user: app_reader

Then register the eyaml backend in the hierarchy, usually as the highest level so secrets always win:

hiera.yaml with the eyaml backend
version: 5
defaults:
  datadir: data
  data_hash: yaml_data
hierarchy:
  - name: "Secrets"
    lookup_key: eyaml_lookup_key
    path: "secrets.yaml"
    options:
      pkcs7_private_key: /etc/puppetlabs/eyaml/keys/private_key.pkcs7.pem
      pkcs7_public_key: /etc/puppetlabs/eyaml/keys/public_key.pkcs7.pem
  - name: "Common"
    path: "common.yaml"

In manifests, encrypted secrets are still read normally with lookup('db_password') — Puppet Server decrypts them at catalog compilation, and agents never see the private key.

Warning

Publish the public key to your team and back up the private key to a safe place off the server. If the private key is lost, every secret encrypted with that key can no longer be decrypted. Never put the private key in a repository.

Conclusion

With Hiera, you move all value decisions from code to centralized data.

  • Hiera 5 provides a data hierarchy — the common, osfamily, and hostname levels are the three tiers used most often.
  • hiera.yaml defines the priority order, and %{facts.os.family} and %{trusted.certname} make it dynamic.
  • lookup fetches values with type control, merge strategies, and safe defaults.
  • Modules carry their own data in the data/ folder, and the environment has the right to override it.
  • hiera-eyaml secures secrets with PKCS7 encryption without changing how manifests read data.

Now manifests only contain "what to do", not "which value to use". In episode 8, you'll see how manifests and data come together into a catalog, learn to order resources with before, require, notify, subscribe, and the -> and ~> chaining arrows, plus the most common catalog errors. See you there!

Learn Puppet - Hiera (Hierarchical Data) | Learn Puppet