Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Puppet code describes the state your infrastructure should have, rather than listing every command an operator must run. You write that description in UTF-8 .pp manifest files; Puppet compiles the code into a node-specific catalog of resources and dependencies, then applies that catalog. A single site.pp is enough to learn the language, but reusable modules become the practical structure as soon as several nodes or services share configuration.

What Puppet code actually does

Puppet is a declarative configuration-management system. Your code states outcomes such as “this package is installed,” “this service is running,” or “this file contains this text.” Puppet evaluates the code, resolves relationships between resources, and compiles a catalog for one node. That catalog is the concrete plan for that node; another node can receive a different catalog even when both are classified by the same manifest.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This distinction matters when reading Puppet code. A manifest can contain conditional logic, variables, and class declarations that apply to many kinds of machines. The result of compilation is still node-specific. Puppet’s own introduction describes it as a tool for managing and automating server configuration.

  • Manifest: a UTF-8 text file ending in .pp that declares resources and Puppet language constructs.
  • Resource: a manageable item such as a package, service, file, user, or scheduled job.
  • Catalog: the compiled collection of resources and dependencies Puppet intends to enforce on one node.
  • Module: a reusable directory tree that packages classes, defined types, data, files, and optional extensions.

Manifests: the starting point for Puppet code

The simplest Puppet deployment has one main manifest, commonly called site.pp. The Puppet main manifest is evaluated first, so it is a natural place to classify nodes and compose higher-level components. In a small experiment you can put resources directly in this file; in a growing codebase, keep it focused on deciding what a node receives and move implementation details into modules.

A minimal manifest

The following manifest ensures that a package is present and its service is enabled and running:

package { 'ntp':
  ensure => installed,
}

service { 'ntpd':
  ensure => running,
  enable => true,
}

Each resource has a type (package or service), a title, and attributes. Puppet’s resource syntax uses a trailing comma after each attribute. Resource relationships can be expressed explicitly or inferred from the order and references you define; explicit relationships are clearer when one resource must precede or trigger another.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Validate and apply a first file

Use a disposable development machine or virtual machine before changing a production node. Save the code as site.pp, then run these checks:

  1. puppet parser validate site.pp checks the Puppet syntax without applying resources.
  2. puppet apply --noop site.pp compiles the manifest and shows the changes Puppet would make. --noop is a dry run; it should not alter managed resources.
  3. puppet apply site.pp applies the catalog when the preview is correct.

On a Puppet-managed server, the main manifest normally lives in the environment’s manifest directory, for example /etc/puppetlabs/code/environments/production/manifests/site.pp. A standalone test can use any readable path and pass that file to puppet apply.

Classes: reusable blocks that must be declared

A class is a named block of Puppet code, usually stored in a module. Defining a class makes its code available; it does not, by itself, add the class’s resources to a catalog. Declaring the class is the step that includes those resources.

class motd {
  file { '/etc/motd':
    ensure  => file,
    content => "Managed by Puppetn",
  }
}

include motd

The class motd block is the definition. include motd declares it. This difference explains a common beginner problem: Puppet accepts a class definition, but no file or package changes appear because the class was never declared.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Classes can accept parameters, which lets one implementation serve several environments:

class webserver(String $listen_address = '0.0.0.0') {
  file { '/etc/example-web.conf':
    ensure  => file,
    content => "listen=${listen_address}n",
  }
}

A site manifest or another class can declare webserver with an appropriate value. Keep defaults safe and document parameters so callers know which behavior they are changing.

How modules organize Puppet code

A module normally manages one technology or one coherent infrastructure task. It is Puppet’s basic packaging unit and is loaded from directories on the modulepath. Besides classes, a module can contain defined types, tasks, plans, functions, custom resource types and providers, facts, data, examples, and static files. You do not need every directory in every module; add only what the component requires.

A conventional module named motd looks like this:

motd/
├── manifests/
│   ├── init.pp
│   └── config.pp
├── files/
│   └── banner.txt
├── templates/
│   └── motd.epp
├── data/
│   └── common.yaml
├── examples/
│   └── init.pp
└── metadata.json

What each directory is for

  • manifests/ contains Puppet language files. Each .pp file normally defines one class or one defined type.
  • init.pp is the conventional entry point for the module’s main class.
  • files/ holds static files that can be delivered with the file resource’s source attribute.
  • templates/ holds rendered templates when configuration must vary by facts, parameters, or data.
  • data/ stores module data for the module’s data-binding system, commonly in YAML.
  • examples/ provides sample declarations for users and for documentation.
  • metadata.json records module identity, dependencies, and compatibility information required by common distribution workflows.

Modules can also expose tasks, plans, functions, facts, and providers when a component needs more than ordinary resources. Those extensions are optional; a small module containing only manifests/init.pp is valid.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Where init.pp goes and how names map to files

Inside a module, the file path and class name follow a predictable convention. For a module directory called motd, manifests/init.pp contains the class motd. A file at manifests/config.pp conventionally contains motd::config.

File Typical class or type name How it is declared
motd/manifests/init.pp motd include motd or a class declaration
motd/manifests/config.pp motd::config include motd::config
motd/manifests/install.pp motd::install include motd::install

The namespace prevents collisions between unrelated modules. If two modules each have a class named config, their fully qualified names can still be distinct, such as motd::config and webserver::config. Keep one primary class or defined type per manifest file so the layout remains discoverable.

Refactor a one-file manifest into a module

Moving code into a module is a structural refactor, not a change in the desired state. Work in small steps:

  1. Identify one logical component. Group resources that install, configure, and run the same service or perform the same infrastructure task.
  2. Create the module tree. Put the directory under a modulepath directory such as modules/motd, and create manifests/.
  3. Move the implementation. Place the component’s main class in manifests/init.pp. Move supporting classes to namespaced files such as manifests/config.pp.
  4. Declare the module from the main manifest. Replace the original resource block with include motd or an explicit parameterized class declaration.
  5. Add static assets and data only when needed. Put unchanging files in files/, rendered content in templates/, and environment-independent defaults in data/.
  6. Run validation and a no-op compile. Validate every changed manifest, then inspect the catalog with puppet apply --noop before applying it.

As the module library grows, leave site.pp responsible mainly for classification and composition. That keeps node-specific decisions visible while allowing implementation classes to be reused across many nodes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One-file manifest or module-based design?

Decision factor Single manifest Module-based design
Reuse across nodes Resources are easy to write once but tend to be copied when another node needs them. Classes and defined types can be declared repeatedly with parameters and namespaces.
Separation of concerns Classification and implementation often become interleaved. The main manifest composes components while each module owns its implementation.
Discoverability Finding a resource may require searching a large file. Standard directories and names such as motd::config reveal where code belongs.
Dependencies and data Relationships and defaults are commonly embedded in one file. Modules can declare dependencies, parameters, data, files, templates, and examples in a predictable package.
Testing and documentation Quick for a prototype, but harder to isolate as it grows. Examples and component boundaries make testing and documentation easier to organize.
Team and Code Manager workflows Works for a very small installation. Fits version-controlled module libraries, Puppetfiles, Code Manager, and r10k workflows.

There is no official quantitative benchmark that makes one structure universally faster. Choose a single manifest for a short-lived experiment; choose modules when reuse, ownership boundaries, testing, or multiple environments matter.

Installing and managing reusable modules

The Puppet Forge is the official repository for reusable modules and contains thousands of modules from Puppet developers and the open-source community. Treat a Forge module as code you are introducing into your infrastructure: inspect its metadata, dependencies, supported operating systems, examples, and release history before deploying it.

Small installation: the puppet module command

For a small or standalone installation, install a module into the configured modulepath with:

puppet module install <author>-<module>

Use puppet module list to see installed modules and their locations. The exact module name is the author and module identifier shown by the Forge; do not substitute a package name from an operating-system repository. After installation, declare the module’s documented class in your manifest and run a no-op compile.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Teams using Code Manager or r10k

Code Manager and r10k installations normally declare dependencies in a Puppetfile. A deployment process resolves that file and populates the environment’s modulepath, keeping module versions reproducible across environments. In this workflow, edit the Puppetfile, commit it with the rest of the control repository, deploy the environment, and then declare the module in classification code. Do not mix ad-hoc workstation installs with an automated pipeline unless you deliberately understand which modulepath will win.

How the modulepath affects loading

Puppet searches the configured modulepath for a directory whose name matches the module. If a class cannot be found, check the environment selected for the node, the modulepath shown by your Puppet configuration, the module directory name, and whether the deployment actually populated that environment. A module installed into a different environment is invisible to the compile you are testing.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Keep the main manifest focused on classification

Classification answers “which nodes receive which components?” For example, a role class might compose several modules:

class role::web {
  include motd
  include webserver
  include monitoring_agent
}

The site manifest can then assign role::web to the appropriate nodes using the classification method your installation uses. This separates node selection from package names, configuration templates, and service relationships. A module can still contain internal classes such as webserver::install and webserver::config; callers normally use its public entry class unless the module documentation says otherwise.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common mistakes and recovery steps

“The class is defined but nothing changed”

Defining a class is not declaring it. Add include class_name or a parameterized class declaration in a manifest that the node actually compiles. Re-run puppet apply --noop and confirm the expected resources appear.

“Could not find class”

Check spelling and namespace first: a file named manifests/config.pp in module motd normally maps to motd::config, not a global config class. Then verify the module directory is on the selected environment’s modulepath and that its deployment completed.

“The module is present but the file is missing”

Static files must be under the module’s files/ directory and referenced with the module source convention documented for your Puppet version. Templates belong under templates/ and require a template reference; placing a template in files/ will not render variables.

Syntax or catalog errors

Run puppet parser validate on each changed manifest before applying it. If syntax succeeds but compilation fails, inspect class parameters, duplicate resource titles, missing dependencies, and conditional branches for the node’s facts. A successful parser check does not guarantee that every referenced class, file, package, or provider is available.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Unexpected production changes

Use a controlled development node, compile with --noop, review the change list, and make one component change at a time. Keep the module and control-repository changes under version control so you can identify and revert the change that introduced the behavior.

A practical learning path

  1. Write a tiny .pp manifest containing one or two resources.
  2. Validate it, compile it in a controlled environment, and inspect the no-op output.
  3. Group the resources for one logical component into a class.
  4. Move that class into a module with the standard manifests/ layout and an init.pp entry point when it is the module’s main class.
  5. Add files, templates, data, examples, or namespaced helper classes only as the component needs them.
  6. Install external modules through the appropriate modulepath, Puppetfile, Code Manager, r10k, or puppet module workflow.
  7. Keep the main manifest or classification layer focused on composing modules for each node role.

Puppet’s documentation expects users to write some modules of their own, even when they also rely on Forge modules. That combination is normal: use community modules for well-understood reusable components and keep organization-specific policy, integration, and classification in modules your team controls.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.