Tracing how Puppet was born: from the era of manual scripting, CFEngine 1993, the birth of Puppet by Luke Kanies in 2005, to Chef and Ansible. Understanding the problems Puppet solves and its position among other tools.

In the previous episode 0, you prepared the prerequisites and environment — from basic Linux and Ruby skills to installing Puppet Server, PDK, and a test node. This time we'll take a step back from the hands-on work and dive into why Puppet exists. We'll trace the history of configuration management, understand the real problems that gave birth to it, and see Puppet's position among Chef, Ansible, and SaltStack.
Why is understanding this history important? As an engineer, you don't just need to know how to write manifests — you also need to know why Puppet was designed the way it is: why it's pull-based, why it's declarative, and why its ecosystem is so complete. Without this context, you'll struggle to judge when Puppet is the right choice and when it isn't.
Before configuration management existed, managing servers was an exhausting, error-prone job. Imagine managing a hundred web servers: every time there's a new configuration, you have to log into each one, run shell scripts or type manual commands, and then hope nothing was missed. Even shell scripts have major weaknesses:
Out of that frustration came the generation of tools called configuration management.
CFEngine by Mark Burgess was the first configuration management tool. It introduced the concept that is now an industry standard: describing the desired state and letting the system keep itself converged to that state. CFEngine is very lightweight and efficient, but its configuration language is fairly primitive and its ecosystem is small.
Luke Kanies built Puppet in 2005 to overcome CFEngine's limitations. Puppet brought two major breakthroughs:
Puppet also introduced a complete ecosystem: a central server that compiles catalogs, agents running on a schedule, and centralized reporting. This is what made Puppet one of the most dominant tools in its early era.
Chef arrived with a different philosophy: code is written in pure Ruby and it follows a client-server model with similar architecture. This "infrastructure as code with a real programming language" approach offers great flexibility, but it has a steeper learning curve because it requires understanding Ruby.
Ansible, by Michael DeHaan, flipped the paradigm: it's push-based and agentless — no agent runs on the nodes, just SSH. This approach is very easy to learn and quick to adopt, which made Ansible popular among those who value simplicity.
| Year | Tool | Pioneer | Model | Distinction |
|---|---|---|---|---|
| 1993 | CFEngine | Mark Burgess | Pull, agent | First tool; policy-based |
| 2005 | Puppet | Luke Kanies | Pull, agent | Declarative, resource model, complete ecosystem |
| 2009 | Chef | Adam Jacob | Pull, agent | Pure Ruby code |
| 2012 | Ansible | Michael DeHaan | Push, agentless | SSH, simple |
Puppet is a model-driven system: everything managed is represented as a resource model with properties, not as step-by-step instructions. A Puppet manifest declares how the system should be — for example, nginx installed at a specific version, the service running, and the config file containing specific content.
The Puppet system is also pull-based: every node runs its agent on a schedule (default 30 minutes), the agent fetches a catalog from the server, then applies it. This model differs from Ansible's push approach — an important difference to understand because it affects how you design your Puppet environment.
package { 'nginx':
ensure => installed,
}
service { 'nginx':
ensure => running,
enable => true,
}Notice: you don't write "run apt install, then run systemctl start". You declare the end state — Puppet determines the steps and checks whether the state has been reached.
The core problem Puppet solves: defining the desired state for each server — packages, services, files, users — and keeping it converged. If a person or another process changes a config file, on the next agent run Puppet will restore it to the declared state. This is what idempotency means in the real world.
Manual scripting won't survive thousands of nodes. Puppet is designed for that scale:
Puppet isn't just a single binary — it's an ecosystem:
| Component | Function |
|---|---|
| Puppet Server | Compiles catalogs from manifests |
| Puppet Agent | Runs on every node, applies the catalog |
| PuppetDB | Centralized facts and report storage |
| Bolt | Orchestration for running on-demand tasks |
| Puppet Enterprise | Console, RBAC, compliance on top of Puppet OSS |
| Aspect | Puppet | Ansible | Chef | SaltStack |
|---|---|---|---|---|
| Model | Pull, agent | Push, agentless | Pull, agent | Hybrid (master + agentless) |
| Language | Ruby-like DSL | YAML + Python | Ruby | YAML/Python |
| Learning curve | Moderate | Low | High | Moderate |
| Reporting | Centralized PuppetDB | AWX/plugins | Chef Automate | Salt master |
| Ideal for | Stable enterprise infrastructure | Ad-hoc & fast automation | Ruby-heavy environments | Large & flexible infrastructure |
We'll dive deeper into the comparisons in later episodes; for now it's enough to understand where each one stands.
In this episode 1, we traced the journey of configuration management: from the non-idempotent era of manual scripting, the birth of CFEngine in 1993, Puppet by Luke Kanies in 2005, to Chef and Ansible that came later. You also understood Puppet's declarative pull-based model and the big problems it solves — desired state, convergence, scaling to thousands of nodes, and a complete ecosystem.
Key takeaways:
In the next episode, episode 2, we'll dissect Puppet's core concepts and main architecture — the agent run flow from Facter gathering facts, SSL certificate authentication, catalog compilation on the server, to reporting back to PuppetDB. Grab a coffee, because this is the most fundamental episode for understanding how Puppet works from the inside!