
September 11, 2026
12 min read
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
nginxmust be installed. - A file at
/etc/nginx/sites-enabled/app.confmust contain specific content. - A service named
nginxmust 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 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.
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
- Download and install the Puppet release package for your Ubuntu codename.
- Enable the Puppet 8 repository (current open-source major line as of 2026).
- Install the
puppet-agentpackage. - Point the agent at your Puppet server hostname.
- 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.
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.
| Criterion | Puppet | Ansible | Chef | Salt |
|---|---|---|---|---|
| Agent required | Yes (agent daemon) | No (SSH by default) | Yes (chef-client) | Optional minion |
| Language style | Declarative DSL | YAML playbooks | Ruby recipes | YAML + Jinja |
| Convergence model | Pull on schedule | Push on demand | Pull on schedule | Both |
| Learning curve | Moderate | Gentle | Steep (Ruby) | Moderate |
| Best fit | Large stable fleets | Small teams, mixed OS | Complex policy code | Speed 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.
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.ppfor a production fleet. - Validate manifests in CI with
puppet parser validatebefore 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
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.

