Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Puppet: Configuration Management Basics

By Kokil Thapa | Last reviewed: September 2026

Puppet: Configuration Management Basics start with a simple idea. You describe the state you want—a package installed, a service running, a config file in place—and Puppet enforces that state on every run. On a production Laravel fleet or a cluster of Linux servers running PHP-FPM and Apache, manual SSH edits do not scale. One missed step on a staging box becomes a production outage. Configuration management replaces tribal knowledge with version-controlled, repeatable code.

Puppet is one of the oldest and most structured tools in this space. It uses a declarative domain-specific language (DSL), a catalog compiler, and an agent that converges each node toward the desired state. This guide covers architecture, first manifests, module layout, and practical trade-offs against Ansible and shell scripts. If you already manage servers for web applications, the patterns here map directly to real stacks.

What is Puppet configuration management and why does it matter?

Configuration management (CM) treats infrastructure the same way you treat application code. Server settings live in Git. Changes go through review. Rollouts are predictable. Puppet was built for this model before containers became the default packaging layer.

At its core, Puppet answers one question: what should this machine look like right now? You do not write imperative steps like "run apt update, then install nginx." You declare resources:

  • A package named nginx must be installed.
  • A file at /etc/nginx/sites-enabled/app.conf must contain specific content.
  • A service named nginx must be running and enabled at boot.

Puppet compares the declared state to reality. It applies only the changes needed. That property is called idempotency. Run the same manifest twice and the second run should change nothing if the system is already correct.

On client projects I maintain with Deployer 7 and GitLab CI, application deploys are separate from OS baseline configuration. Puppet (or a similar CM tool) owns the baseline: users, packages, firewall rules, PHP versions, log rotation. Deployer owns the Laravel release symlink swap. Splitting those concerns keeps deploys fast and server hardening consistent.

Puppet Configuration Management ArchitecturePuppet ServerCompiles catalog from manifestsAgent Node AWeb + PHP-FPMAgent Node BQueue workerAgent Node CDatabase replicaGit RepositoryManifests, modules, Hiera data — single source of truth
Puppet configuration management basics: the server compiles desired state; agents enforce it on each node.

Puppet fits teams that manage dozens or hundreds of long-lived VMs. It is less ideal for ephemeral containers where image builds replace ongoing convergence. For traditional VPS hosting—common on Nepal business sites on Ubuntu EC2—Puppet still earns its place.

How does the Puppet master-agent architecture work?

Most production Puppet deployments use a master-agent (server-agent) model. The Puppet server holds your code. Each managed node runs the Puppet agent daemon. On a schedule—typically every 30 minutes—the agent collects facts, sends them to the server, receives a compiled catalog, and applies it.

Facts, catalogs, and reports

Facts are key-value data about the node: hostname, IP address, operating system, memory, custom facts you define. Puppet ships with Facter for fact collection. Facts drive conditional logic—install PHP 8.3 on Ubuntu 24.04 but 8.2 on 22.04, for example.

The server merges manifests, module code, and Hiera data with those facts. It produces a catalog: an ordered list of resources the agent must enforce. After apply, the agent sends a report back. Reports feed into tools like PuppetDB or external dashboards.

Smaller setups can run Puppet apply in stand-alone mode. One machine compiles and applies locally. That is useful for learning or single-server labs. It skips central reporting and cross-node coordination.

Puppet Agent Run Cycle1. CollectFacter facts2. CompileBuild catalog3. ApplyEnforce state4. ReportSend resultsIdempotent convergenceSecond run changes nothing if system matches catalogDrift from manual edits is corrected on next agent runReports show changed, unchanged, and failed resources
Each Puppet agent run collects facts, receives a catalog, applies resources, and reports outcomes.

Understanding this loop matters when you debug. If a config file keeps reverting, Puppet is doing its job. Someone edited the file by hand. Either move the change into a manifest or disable management for that resource—never fight the tool silently.

For deeper comparison with push-based tools, see the dedicated write-up on Ansible vs Puppet vs Chef vs Salt. Puppet pulls catalogs on a schedule. Ansible pushes tasks when you trigger a playbook.

How do you install Puppet and run your first manifest on Ubuntu?

Puppet publishes open-source packages through its own apt repository. On Ubuntu 22.04 or 24.04—the versions I use on production EC2 hosts—the install path is straightforward. Always match agent and server major versions.

Install the Puppet agent

  1. Download and install the Puppet release package for your Ubuntu codename.
  2. Enable the Puppet 8 repository (current open-source major line as of 2026).
  3. Install the puppet-agent package.
  4. Point the agent at your Puppet server hostname.
  5. Sign the certificate request on the server, then trigger a test run.
# On Ubuntu 24.04 (noble) — agent node
wget https://apt.puppet.com/puppet8-release-noble.deb
sudo dpkg -i puppet8-release-noble.deb
sudo apt update
sudo apt install puppet-agent

# Point agent at your Puppet server
sudo /opt/puppetlabs/bin/puppet config set server puppet.example.com --section agent

# Enable and start the agent
sudo systemctl enable puppet
sudo systemctl start puppet

# On the Puppet server — sign the cert
sudo /opt/puppetlabs/bin/puppetserver ca sign --certname web01.example.com

# Test run (agent)
sudo /opt/puppetlabs/bin/puppet agent -t

The -t flag runs in noop-safe test mode on older setups; on current agents it performs a single apply run and logs changes. Read the output carefully before enabling scheduled runs site-wide.

A minimal first manifest

Place code in the server environment—commonly /etc/puppetlabs/code/environments/production/manifests/site.pp. A hello-world manifest ensures NTP or chrony is running:

node 'web01.example.com' {
  package { 'chrony':
    ensure => installed,
  }
  service { 'chrony':
    ensure => running,
    enable => true,
    require => Package['chrony'],
  }
}

Resource titles matter. Package['chrony'] references the package resource by its title. require builds ordering so the package installs before the service starts. Puppet also supports before, notify, and subscribe for dependency chains—similar in spirit to systemd ordering but declared in code.

Validate syntax before deploy:

puppet parser validate site.pp
puppet apply --noop site.pp

Treat manifest repos like application code. Run validation in CI. Pair Puppet with the same GitLab pipeline mindset you use for release management and changelogs. A broken manifest can disable SSH or wipe a config directory across the fleet.

How do you structure Puppet modules for a PHP or Laravel server?

Real stacks need modules—not one giant site.pp. A module bundles classes, defined types, templates, and files under a namespace. For a Laravel host you might maintain modules named php, apache, redis, and deploy_user.

Module anatomy

Standard layout on the Puppet server:

modules/
  php/
    manifests/
      init.pp          # class php
      fpm_pool.pp      # defined type php::fpm_pool
    templates/
      fpm-pool.conf.epp
    files/
      99-custom.ini
  apache/
    manifests/
      init.pp
      vhost.pp

A class is included once per node. A defined type can be instantiated many times—one FPM pool per application, for example.

class php (
  String $version = '8.3',
) {
  package { "php${version}-fpm":
    ensure => installed,
  }
  package { "php${version}-mysql":
    ensure => installed,
  }
  service { "php${version}-fpm":
    ensure  => running,
    enable  => true,
    require => Package["php${version}-fpm"],
  }
}

Templates use EPP (Embedded Puppet) or legacy ERB. They inject variables into config files—Apache vhosts, PHP-FPM pools, Redis bind addresses. Keep secrets out of templates. Pull them from Hiera or a secrets manager like Vault.

Hiera for data separation

Hiera separates data from logic. You store YAML keys like php::version or apache::vhosts per environment, per role, or per node. Manifests reference lookup('php::version') instead of hard-coding values.

# data/nodes/web01.example.com.yaml
php::version: '8.3'
apache::server_name: 'app.example.com'

# In a manifest
$php_version = lookup('php::version', String)

This pattern scales when you run PHP 8.3 on new Laravel 13 hosts but keep PHP 8.2 on legacy Laravel 12 boxes. Change data, not class logic. Validate YAML with a JSON or YAML formatter in CI before merge.

On sister sites I maintain with Deployer 7, OS packages and PHP-FPM pools are baseline Puppet concerns. Application releases stay in Deployer. That split mirrors how Notary Kathmandu and related legal-tech portals share infrastructure patterns without sharing application code.

Puppet Module and Hiera LayersManifestsClasses and typesResource declarationsTemplatesEPP config filesApache, PHP-FPMFilesStatic assetsKeys, scriptsHiera Data Hierarchycommon → environment → role → nodelookup() merges keys into compiled catalog
Puppet modules combine manifests, templates, and files; Hiera supplies environment-specific data.

Reuse community modules from the Puppet Forge when they match your OS versions. Pin module versions in a Puppetfile. Upgrading the upstream puppetlabs-apache module without testing can break vhost syntax overnight.

How does Puppet compare with Ansible, Chef, and Salt?

No configuration management tool wins every scenario. Puppet excels when you want strong modelling, scheduled convergence, and a central source of truth. Ansible wins when you want SSH push runs, low agent overhead, and quick ad-hoc tasks.

CriterionPuppetAnsibleChefSalt
Agent requiredYes (agent daemon)No (SSH by default)Yes (chef-client)Optional minion
Language styleDeclarative DSLYAML playbooksRuby recipesYAML + Jinja
Convergence modelPull on schedulePush on demandPull on scheduleBoth
Learning curveModerateGentleSteep (Ruby)Moderate
Best fitLarge stable fleetsSmall teams, mixed OSComplex policy codeSpeed at scale

For a three-server Laravel shop in Kathmandu, Ansible plus Deployer is often enough. For thirty application servers with strict compliance baselines, Puppet's reporting and catalog model pays back the agent overhead.

Puppet also complements provisioning tools. Terraform creates the VM and network. Puppet configures the OS inside it. That boundary is covered in the Terraform vs Ansible provisioning guide. Do not use Puppet to create cloud instances unless you accept slower feedback loops.

When to Choose PuppetHow many servers?1–5 nodesAnsible or scripts OK10+ nodesPuppet earns its keepNeed audit reports?Scheduled drift correctionChoose Puppet CMEphemeral containers?Build images insteadSkip long-lived agents
Decision guide for Puppet configuration management basics: fleet size and compliance needs drive the choice.

Operational practices that survive production

Puppet is not fire-and-forget. Schedule maintenance windows for module upgrades. Monitor agent run failures through centralized log management. Tag nodes by role—web, worker, db—using trusted facts or an external inventory.

Integrate Puppet with your security baseline. Enforce fail2ban rules, SSH hardening, and automatic security patches through modules. Pair that with vulnerability management automation so OS-level fixes do not depend on someone remembering to SSH in.

For PHP production tuning—opcache settings, FPM pool sizes—Puppet keeps configs identical across nodes. That matters when you chase bugs that only appear on one server because its ini file drifted. Read PHP OPcache configuration for production for the values worth encoding in manifests.

Official reference material lives in the Puppet 8 open-source documentation. Bookmark the language guide and the resource type reference. They are authoritative when syntax questions come up during code review.

Key Takeaways

  • Puppet configuration management basics boil down to declarative manifests, a compiled catalog, and idempotent agent runs that correct drift.
  • Split OS baseline (Puppet) from application deploys (Deployer, GitLab CI) so you change each layer at the right speed.
  • Organize code into modules and Hiera data—never one monolithic site.pp for a production fleet.
  • Validate manifests in CI with puppet parser validate before they touch dozens of nodes.
  • Choose Puppet for larger, long-lived fleets that need scheduled convergence and audit reports; use Ansible for smaller ad-hoc setups.
  • Pin Forge module versions and test upgrades on a canary node—upstream module changes break vhosts and FPM pools silently.

People Also Ask

Is Puppet free to use?

Puppet open source (Puppet Agent and Puppet Server) is free under an open-source license. Puppet Enterprise adds commercial support, role-based access control, and orchestration features at a subscription cost. Most small and mid-size teams start with open source on self-managed Ubuntu servers.

What is the difference between a Puppet manifest and a module?

A manifest is a .pp file containing resource declarations. A module is a directory that bundles manifests, templates, files, and metadata under one name like apache or php. Modules are reusable units you publish internally or download from the Forge.

Does Puppet work with Docker and Kubernetes?

Puppet can configure container hosts and install Kubernetes components, but ephemeral pods are usually configured at image build time. For long-lived node pools—bare metal or VM workers—Puppet still enforces OS-level baselines while Kubernetes handles pod scheduling.

How often should the Puppet agent run?

The default interval is 30 minutes. Sensitive environments sometimes run every 15 minutes for faster drift correction. Very frequent runs increase server load without much benefit if your stack changes slowly. Adjust based on report volume and compliance requirements.

Put Puppet to work on your fleet

Puppet: Configuration Management Basics are the foundation for any team outgrowing manual server edits. Start with one module—chrony, UFW, or PHP-FPM—on a staging node. Add Hiera once you have more than a handful of variables. Expand to role-based classes as your enterprise application footprint grows.

If you run a mixed fleet and want help drawing the line between Puppet, Ansible, and your existing deploy pipeline, contact us for a infrastructure review. You can also browse the Adventure Third Pole Trek portfolio for an example of Laravel plus disciplined server operations, or explore ongoing support and maintenance for sites that need baseline automation without a full-time ops hire.

Frequently Asked Questions

Puppet is declarative configuration management: you describe desired server state in manifests, a server compiles a catalog, and agents apply it idempotently on a schedule to correct drift automatically.

Idempotency means running the same manifest twice produces no changes if the system already matches the declared state. Puppet compares desired state to reality and applies only what is missing or wrong. That property matters on production fleets because scheduled agent runs safely re-enforce baselines without duplicating work. If someone hand-edits a managed config file, the next run reverts it—which is intentional drift correction, not a bug.

In the standard model, the Puppet server holds your code. Each managed node runs a Puppet agent daemon. On a schedule—typically every 30 minutes—the agent collects facts via Facter, sends them to the server, receives a compiled catalog of ordered resources, applies them, and sends a report back. Reports feed PuppetDB or external dashboards. Smaller setups can use puppet apply in stand-alone mode, where one machine compiles and applies locally without central reporting.

Install from Puppet’s apt repository and match agent and server major versions. On Ubuntu 24.04, download puppet8-release-noble.deb, run dpkg -i, apt update, then apt install puppet-agent. Point the agent at your server with puppet config set server puppet.example.com --section agent, enable and start the systemd unit, sign the certificate on the server with puppetserver ca sign, then test with puppet agent -t before enabling fleet-wide scheduled runs.

A manifest is a .pp file containing resource declarations—packages, files, services—with titles and relationships like require or notify. A module is a reusable directory namespace bundling manifests, templates, files, and metadata under one name such as php or apache. Production stacks use modules, not one giant site.pp. Community modules come from the Puppet Forge; pin versions in a Puppetfile because upstream upgrades can break vhost or FPM pool syntax overnight.

Split concerns into modules like php, apache, redis, and deploy_user. A class such as php is included once per node and installs php8.3-fpm, extensions, and the service. Defined types like php::fpm_pool instantiate multiple pools per application. Templates in EPP or ERB inject variables into Apache vhosts and FPM configs. Keep secrets out of templates—pull them from Hiera or Vault. On real stacks, Puppet owns OS baseline packages and pools while Deployer handles Laravel release symlink swaps.

Hiera separates data from manifest logic. You store YAML keys such as php::version or apache::server_name per environment, role, or node under data/nodes/web01.example.com.yaml. Manifests call lookup('php::version', String) instead of hard-coding values. That scales when new Laravel 13 hosts run PHP 8.3 while legacy Laravel 12 boxes stay on PHP 8.2—you change data, not class logic. Validate YAML in CI before merge, the same way you gate application code.

Puppet uses a declarative DSL, requires an agent daemon, and pulls catalogs on a schedule—strong for large stable fleets needing convergence and audit reports. Ansible uses YAML playbooks, pushes over SSH by default with no agent, and suits ad-hoc or smaller mixed-OS setups. For a three-server Laravel shop, Ansible plus Deployer is often enough. For thirty application servers with strict compliance baselines, Puppet’s catalog model and reporting justify the agent overhead. Neither replaces provisioning tools like Terraform, which should create VMs while Puppet configures the OS inside them.

Puppet open source—Puppet Agent and Puppet Server—is free under an open-source license. Puppet Enterprise adds commercial support, role-based access control, and orchestration at a subscription cost. Most small and mid-size teams start with open source on self-managed Ubuntu servers.

The default interval is 30 minutes. Sensitive environments sometimes use 15 minutes for faster drift correction. Very frequent runs increase server load without much benefit if your stack changes slowly—adjust based on report volume and compliance requirements.

Puppet can configure container hosts and install Kubernetes components, but ephemeral pods are usually configured at image build time rather than through ongoing agent convergence. For long-lived node pools—bare metal or VM workers—Puppet still enforces OS-level baselines while Kubernetes handles pod scheduling. Puppet fits traditional VPS hosting on Ubuntu EC2; it is less ideal for ephemeral containers where image builds replace scheduled convergence.

Treat manifest repos like application code and run validation in CI. Use puppet parser validate site.pp to catch syntax errors before merge. Test with puppet apply --noop site.pp to preview changes without applying them. Read puppet agent -t output carefully on a staging node before enabling scheduled runs site-wide. A broken manifest can disable SSH or wipe a config directory across the fleet—pair Puppet with the same GitLab pipeline discipline you use for release management.

Facts are key-value data about a node collected by Facter: hostname, IP, operating system, memory, and custom facts you define. The Puppet server merges manifests, module code, and Hiera data with those facts to compile a catalog. Facts drive conditional logic—install PHP 8.3 on Ubuntu 24.04 but 8.2 on 22.04, for example. Tag nodes by role using trusted facts or external inventory. When debugging, if a config keeps reverting, Puppet is enforcing declared state against manual edits.

Split the layers. Puppet should own the OS baseline: users, packages, firewall rules, PHP versions, FPM pools, log rotation, fail2ban, and SSH hardening. Application deploys stay in Deployer 7 and GitLab CI with Laravel release symlink swaps. That separation keeps deploys fast while server hardening stays consistent across nodes. On sister sites I maintain, OS packages and PHP-FPM pools are Puppet concerns; application releases never mix into manifests. Do not use Puppet to create cloud instances—Terraform provisions VMs; Puppet configures inside them.

Puppet is not fire-and-forget. Schedule maintenance windows for module upgrades and monitor agent run failures through centralized logging. Pin Forge module versions in a Puppetfile and test upgrades on a canary node first. Integrate security baselines—fail2ban, SSH hardening, automatic patches—through modules rather than manual SSH. Keep PHP opcache and FPM pool configs identical across nodes so bugs do not hide on one drifted server. Bookmark the Puppet 8 open-source language guide and resource type reference for authoritative syntax during code review.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: