In a Puppet manifest, does the order you declare resources decide the order they are applied? How would you make a service restart when its config file changes?
answer
- a graph, not a script
- manifest order is a tiebreaker only
- four metaparameters, two mirrored pairs
- refresh fires only on actual change
- some edges are added for you
basics
~20 sPuppet applies a dependency graph, not the file top to bottom. Ordering comes from require, before, notify and subscribe, the chaining arrows, and autorequire; notify or subscribe also sends a refresh event, which is what restarts a service after its config changes.
solid answer
~50 sA catalog is a graph of resources plus edges, so declaration order is not the contract. Since Puppet 4 the default `ordering` setting is `manifest`, meaning resources with no relationship between them are applied in evaluation order — but that is a tiebreaker, not something to design around, because evaluation order across classes and modules is not yours to control. Real ordering comes from the relationship metaparameters `require` and `before`, from the chaining arrows `->` and `~>`, and from **autorequire**, where a type adds implicit edges on its own (a `file` autorequires its parent directory, for example). For the restart case you need a *refresh* relationship, not just ordering: put `notify => Service['nginx']` on the file, or `subscribe => File['/etc/nginx/nginx.conf']` on the service. That sends a refresh event only when the file actually changes, and the service restarts.
code
puppet · 15 linespackage { 'nginx':
ensure => installed,
}
file { '/etc/nginx/nginx.conf':
ensure => file,
content => "worker_processes 4;\n",
require => Package['nginx'],
notify => Service['nginx'],
}
service { 'nginx':
ensure => running,
enable => true,
}go deeper
Know the four metaparameters by name and what each does, and be able to write the package/file/service trio with require and notify correctly wired.
Explain that the catalog is an acyclic graph, that manifest ordering is only a tiebreaker between unrelated resources, and that a refresh event fires only when the source resource actually changed.
Show judgment about where edges belong — subscribe on the service keeps restart knowledge in one place — and be ready to diagnose a dependency cycle caused by an explicit edge fighting an autorequire.
Frame ordering as a module interface concern: relationships that cross module boundaries should be expressed between classes or via documented anchors, not by reaching into another module's resource titles.
## The catalog is a graph A Puppet manifest looks like a script, which is exactly why this question catches people. What the server compiles is a **directed acyclic graph**: nodes are resources, edges are relationships. The agent applies it in a topological order consistent with those edges. If you never declare an edge, you have not expressed a requirement — you have only expressed a wish about the layout of your text file. There is a nuance worth stating precisely. Puppet's `ordering` setting has defaulted to `manifest` since Puppet 4, which means resources that have no relationship between them are applied in the order they were evaluated. That makes simple manifests behave the way beginners expect. It is still a poor thing to rely on: evaluation order across classes, across modules, and across whatever a future refactor moves around is not under your control, and the moment someone wraps your resources in a class or an `include`, the assumption evaporates. ## The four relationship metaparameters Declared *on* a resource, pointing at another resource by its capitalized reference (`Package['nginx']`): - `require => Package['nginx']` — apply me after that. - `before => Service['nginx']` — apply me before that. - `subscribe => File['/etc/nginx/nginx.conf']` — apply me after that, **and refresh me if it changed**. - `notify => Service['nginx']` — apply me before that, **and refresh it if I changed**. So `require`/`before` are pure ordering, and `subscribe`/`notify` are ordering plus a refresh event. They come in mirrored pairs: `notify` and `subscribe` express the same edge from opposite ends, and which one you write is a style choice about where the knowledge belongs. ```puppet file { '/etc/nginx/nginx.conf': ensure => file, content => "worker_processes 4;\n", require => Package['nginx'], notify => Service['nginx'], } ``` ## Refresh is not the same as ordering A refresh event fires **only when the source resource actually changed** during this run. That is the whole point: on the hundreds of runs where the config file is already correct, no event is sent and the service is not bounced. Ordering alone would never restart anything. What "refresh" means depends on the type. A `service` restarts (or reloads, if you set `restart` or use a `reload`-aware provider). An `exec` does nothing at all unless it is declared `refreshonly => true`, in which case the refresh is the only thing that ever runs it. Types that cannot be refreshed simply ignore the event. ## Chaining arrows The same edges can be written between resource references, which reads well for a straight package/config/service pipeline: ```puppet Package['nginx'] -> File['/etc/nginx/nginx.conf'] ~> Service['nginx'] ``` `->` is ordering; `~>` is ordering plus refresh. Arrows can also chain whole classes, which is the common way to sequence a role: `Class['app::install'] -> Class['app::config'] ~> Class['app::service']`. ## Autorequire: the edges you did not write Many resource types add implicit dependencies on their own. A `file` autorequires its parent directory and the user and group that own it; an `exec` autorequires the user it runs as and its `cwd`. These edges only apply when both resources are in the catalog, and they are silent — nothing warns you that they exist. They are also *only* requirements, never notifications: autorequire will never restart your service for you. The practical lesson is that autorequire covers the boring structural cases so your manifests stay readable, but every relationship that carries meaning — install before configure, restart on change — you must state. ## Cycles fail the run Because the graph must be acyclic, a mutual dependency is a hard error: Puppet reports a dependency cycle and the run stops before applying anything. Two classes that each declare `before` the other, or a `require` added to "be safe" on top of an existing autorequire in the opposite direction, are the usual causes. Running with `--graph` writes the dependency graph out so you can find the loop. ## The failure that gets people in production A package, a config file and a service with no relationships declared. It works in the module author's test because manifest ordering happened to line up, then fails on a fresh node where the service resource was evaluated from a different class first and started against a config file that did not exist yet. Declare the edges.
- What is the difference between `notify` and `subscribe`?None, as far as the catalog is concerned — they declare the same edge from opposite ends. `notify` on resource A pointing at B says "apply me first and refresh B if I changed"; `subscribe` on B pointing at A says exactly the same thing. The choice is about ownership: a module that manages a service usually has the service `subscribe` to config files, so the service resource stays the single place that knows what bounces it.
- What does a refresh event actually do to an `exec` resource?Nothing, unless the `exec` is declared `refreshonly => true`. A plain `exec` runs whenever its `onlyif`/`unless`/`creates` conditions say it should, and ignores refreshes. With `refreshonly => true` the command runs *only* when a refresh arrives, which is the idiomatic way to express "regenerate this artifact after the source file changed".
- You add `require` to a file resource and the run now fails with a dependency cycle. What happened?You most likely duplicated an edge that autorequire already added in the opposite direction — a file autorequires its parent directory, so requiring the file from the directory closes the loop. The graph must be acyclic, so Puppet aborts before applying anything. Run with `--graph`, inspect the generated dependency graph, and delete the explicit edge rather than fighting the implicit one.
saying these in an interview costs you the question
- Says resources always run top to bottom like a script
- Uses require and expects the service to restart
- Thinks a refresh fires on every run, not on change
- Assumes autorequire also handles notifications
- Believes a dependency cycle just gets broken automatically