Comparison

Ansible vs Chef vs Puppet

All three converge servers on a declared state, and any of them can install a package, write a template and restart a service. What separates them is the operating model: who starts a run, what has to be reachable from where, what happens when nobody starts anything, which servers you have to run to use the tool, and, in 2026, who owns the roadmap and what a production licence costs. The sections below take those in turn, with the code kept to the lines where the models show.

In short — Ansible pushes from a control node over SSH or WinRM with no agent and no server; nothing happens unless something invokes it. Chef Infra Client and Puppet agent run on every node and pull from a server on a schedule (1800 seconds plus splay for Chef, 30 minutes for Puppet), so drift is corrected without anyone acting. Ansible is GPL-3.0 with a commercial platform above it; Chef products need a commercial licence for production; Puppet Core and Enterprise are proprietary Perforce products (the lifecycle page says so as of August 2026), with OpenVox as the community continuation.
Ansible vs Chef vs Puppet: summary by dimension
DimensionAnsibleChefPuppet
Execution modelPush: a control node connects to managed nodes over SSH or WinRMPull: chef-client on each node fetches policy from Chef Infra Server and convergesPull: puppet agent sends facts, receives a compiled catalog from Puppet Server and applies it
Default cadenceNone; runs when invoked (ansible-pull via cron inverts this, documented as rare and not recommended)Every 1800 seconds plus a random splay when daemonizedruninterval 30m; a report is sent after every run
LanguageYAML plays and tasks; modules mostly Python; Jinja2 templatesRuby DSL recipes and resources; compile phase then converge phaseDeclarative Puppet DSL compiled into a per-node catalog
Ordering and change propagationTasks run in listed order; handlers run once at the end of the playResources run in recipe order; notifications are delayed to the end of the run by defaultManifest order unless relationships are declared; notify and subscribe send refresh events
Dry runansible-playbook --check --diffchef-client --why-runpuppet agent --noop (also a setting)
Servers you operateNone required; AWX or Ansible Automation Platform for scheduling and RBACChef Infra Server (Erlang API, PostgreSQL, Bookshelf); Chef Automate optionalPuppet Server with built-in CA; PuppetDB optional but needed for exported resources and history
On the managed nodePython plus an SSH user with a POSIX shell; PowerShell on Windows; nothing installedChef Infra Client (bundled Ruby); Agentless Mode covers Linux targets over SSHpuppet-agent (Facter and Hiera included); Bolt runs agentless over SSH or WinRM
Current versions (Sep 2026)ansible-core 2.21 (GA May 2026); community package 14.xChef Infra Client 19.3.15 (May 2026)Puppet Core 8.22.0 and 9.1.0; OpenVox 8.29.0
Licence and vendorGPL-3.0 (ansible-core); Red Hat sells Ansible Automation PlatformSource Apache-2.0; distributions under the Chef EULA, free tier for non-production onlyPuppet Core and PE proprietary (Perforce); free for up to 25 nodes in test or dev; OpenVox community fork under Vox Pupuli
Published Jun 1, 2025·Updated ·By SecOpsLog

Three operating models, not three syntaxes

The usual summary (Ansible is easy, Puppet scales, Chef is flexible) is a description of first impressions, not of what you will operate. The useful cut is who starts a run. Ansible runs from a control node, "the machine from which you run the Ansible CLI tools", and "is not normally installed on managed nodes"; the installation guide states "No daemons or database setup are required". Chef installs Chef Infra Client on every node, and "periodically, Chef Infra Client contacts the Chef Infra Server to retrieve the latest cookbooks", converging only if the node has drifted. Puppet installs puppet-agent on every node; the agent sends facts, "the primary server uses [them] to compile a catalog", the agent applies it and "sends a report back to the primary server".

That single difference drives the rest of this page. A push tool needs the control node to reach every host and hold credentials for it, and does nothing between runs. A pull tool needs every host to reach a server, carries its own identity, and keeps working while you sleep. Both Chef and Puppet also ship an agentless option for the cases push suits (Chef's Agentless Mode, Puppet's Bolt), and Ansible ships ansible-pull for the reverse, but each is documented as the secondary path.

Who talks to whom: the network and credential picture

Direction of the connection, per tool

ToolWho connects to whomWhat must existWhere credentials concentrate
AnsibleThe control node opens SSH (or WinRM) to each managed nodeInbound SSH on every host, a user with a POSIX shell and sudo, Python on the targetOn the control node: keys, vault passwords, inventory
Chefchef-client on each node calls Chef Infra Server over HTTPSOutbound HTTPS from every node and an RSA client key registered with the serverOn the server: cookbooks and policy; a key pair per node
Puppetpuppet agent calls Puppet Server over HTTPS (port 8140 by default)Outbound 8140 from every agent and a certificate signed by the built-in CAOn the server: environments, Hiera data and the CA

Ansible's requirements are the host's own: "a user account that can connect through SSH to the node with an interactive POSIX shell" and Python to run "Ansible-generated Python code" (network modules are the exception). The credential concentration is on the control node, which holds keys or vault passwords for everything it manages; that is the machine to protect, and the reason larger estates put AWX or Ansible Automation Platform in front of it rather than a laptop.

Chef and Puppet invert the exposure. Nodes initiate; the server never needs inbound access to them. Chef Infra Client authenticates to the server "using RSA public key-pairs each time" it needs data, and Puppet "includes a built-in certificate authority for managing certificates", with servers and agents communicating "by HTTPS using SSL certificates". The cost is that the server becomes the thing that can change every node at once: whoever can upload a cookbook or merge a Puppet environment has fleet-wide write access, on the agents' schedule, without a human in the loop for each host.

Cadence and drift: what happens when nobody runs anything

Ansible: nothing. A playbook applies when invoked, and a host that someone edited by hand stays edited until the next invocation. Scheduling is external (AWX, AAP, cron, CI). ansible-pull exists to invert this, running "on each managed node, each set to run via cron" against a Git checkout, but the concepts page calls that setup "rare and not the recommended setup", and the CLI tools "are not designed to run concurrently with themselves".

Chef: every 1800 seconds. The --interval default is 1800, --splay adds "a random number between zero and splay" to spread load on the server, and both apply before each run. A node that drifted converges back within roughly half an hour, and every converge uploads run data to the server.

Puppet: every 30 minutes. runinterval has a documented default of 30m (and "a runinterval of 0 means "run continuously" rather than "never run""), splay is off by default and only delays the first run after a service restart, and report is true by default so every transaction reports to the server. noop mode is a setting as well as a flag: Puppet "will take no action, and will instead report the changes it would have made".

This is the difference that matters for compliance and for outages in equal measure. Continuous enforcement means a hand-fix on a Chef or Puppet node is reverted within the interval unless the code changes too; that is the point, and it is also why incident responders need a way to stop the agent or set noop before touching a box. With Ansible the fix persists, and so does whatever else drifted.

The code, where the models show

The three snippets below manage the same thing: a package, a rendered config file, and a service that restarts when the file changes. They are trimmed to the lines that differ in model, not in syntax.

Ansible: ordered tasks, one handler at the end of the play
- hosts: web
become: true
tasks:
- name: nginx present
ansible.builtin.package:
name: nginx
state: present
- name: render config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx # queued, runs once, at the end
handlers:
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
Chef: resources in recipe order, notification delayed by default
package 'nginx'
template '/etc/nginx/nginx.conf' do
source 'nginx.conf.erb'
notifies :reload, 'service[nginx]' # :delayed unless you say :immediately
end
service 'nginx' do
action [:enable, :start]
end
# Ruby is available when the DSL is not enough; that is the escape hatch
# and the maintenance burden in one line.
Puppet: a catalog of resources with declared relationships
package { 'nginx':
ensure => installed,
}
file { '/etc/nginx/nginx.conf':
ensure => file,
content => template('profile/nginx.conf.erb'),
require => Package['nginx'],
}
service { 'nginx':
ensure => running,
enable => true,
subscribe => File['/etc/nginx/nginx.conf'], # refresh on change
}
# manifest order is the default; relationships are what you rely on

Read the propagation lines. Ansible handlers "only run when notified" and run once at the end of the play "regardless of how many tasks notify it"; you flush them early with meta: flush_handlers. Chef notifications are ":delayed" by default, "queued up, and then executed at the end of a Chef Infra Client run", with :immediately and :before available per notification. Puppet resources refresh through notify, subscribe or the ~> arrow, and the built-in types that can refresh are "service, exec, and package". All three converge on the same idea; they differ in whether the ordering is the file order (Ansible, Chef) or a graph where manifest order is only the default and "to manage a group of resources in a specific order, explicitly declare such relationships" (Puppet).

The other thing the snippets show is where a general-purpose language sits. Chef puts Ruby inside the recipe: "the full power of Ruby is available for when you need a programming language". Puppet keeps the DSL declarative and pushes logic into functions, templates and Hiera data. Ansible keeps YAML declarative and pushes logic into Jinja2 expressions and Python modules. Teams that reach for the escape hatch often will feel most at home in Chef, and will also carry the most code that needs Ruby readers to maintain.

Server components you have to run

Ansible needs none. The control node is a machine with Python; everything else is optional, and "multiple control nodes are possible, but Ansible itself does not coordinate across them", which is the gap AWX and the commercial Ansible Automation Platform fill with scheduling, credentials and RBAC. Execution Environments package Ansible into containers so that the control node is reproducible.

Chef needs Chef Infra Server: an API "written in Erlang", PostgreSQL as "the data storage repository", Bookshelf for cookbook content, and a search index. Policy is Policyfile.rb resolved on the workstation into a Policyfile.lock.json that is uploaded and "used in each Chef Infra Client run that's managed by that particular policy name and policy group", which is the modern replacement for mutable roles and environments. Chef Workstation ships the testing tools (Cookstyle, ChefSpec, Test Kitchen); Chef Automate is the optional visibility layer. Local mode (chef-client -z) runs against an in-memory chef-zero for development.

Puppet needs Puppet Server, which "performs the role of the primary node and also runs an agent to configure itself", plus its built-in CA. PuppetDB "is an optional component, which is not required to run Puppet Server", but "is necessary for features that require access to historical data (for example, when exported resources are used)". Hiera separates data from code and ships inside puppet-agent along with Facter. Bolt is the agentless side: it "connects directly to remote targets with SSH or WinRM, so you are not required to install any agent software". The Puppet course covers the server, PuppetDB and Hiera setup hands-on.

Licensing and who owns the roadmap in 2026

This section is documented fact, and it changed recently enough that older comparisons are wrong. ansible-core is GPL-3.0 and community-maintained on a published schedule: 2.21 reached GA in May 2026 with critical fixes to November 2026 and security fixes to May 2027; control nodes need Python 3.12 to 3.14 and targets 3.9 to 3.14. Red Hat sells Ansible Automation Platform on top; the engine itself carries no production restriction.

Chef's source is Apache-2.0, but "before you can use distributions of Chef products, you must accept the Chef End User License Agreement", and Progress Chef's tiers are explicit: the Free tier covers "non-production workloads" for "personal and non-commercial use", Trial is 30 days non-production, and Commercial is what covers "production and non-production workloads". The licence is enforced at download, not at agent runtime, and a licence key is required to download binaries from the Chef portal or to run workflows that download packages at runtime such as knife bootstrap. Chef Infra Client 19.3.15 (May 2026) is the current release.

Puppet moved furthest. Perforce's lifecycle page states: "As of August 2026, Perforce develops and supports Puppet Core versions 8 and 9 as a proprietary product. As of April 2025, open source Puppet 7 and Puppet Core 7 are end of life (EOL)." The purchasing page says it directly: "To use Puppet Core in a production environment and enjoy the full range of features and benefits, purchase the product", and a free version is available "on a maximum of 25 nodes" for testing and development after signing an EULA. The community answer is OpenVox, hosted by Vox Pupuli. It began as a package mirror "to continue providing community packages when Perforce discontinued public packaging efforts in late Fall of 2024", describes itself as "a soft-fork" intended to keep downstream compatibility, and its own release notes state that "Puppet Open Source is no longer actively developed". OpenVox 8.29.0 was released on 4 September 2026 as a bug-fix and security release, alongside OpenVox Server, OpenVoxDB and OpenBolt.

What this means for a new estate
Inference, stated as such: the Puppet language and module ecosystem are not going away, but "Puppet" now means a purchase from Perforce or a community fork less than two years old. Chef in production means a commercial agreement with Progress. Ansible in production means GPL-3.0 tooling with an optional commercial platform. Price the licence in before you price the engineering.

Six fleets, three answers

Which model, by fleet

Fleet and situationPickWhyWhat you absorb
A few hundred hosts, mixed Linux and Windows, run by a team that also does deployments and ad hoc operationsAnsibleOne tool for orchestration and configuration; no agents to roll out; SSH and WinRM are already thereNo enforcement between runs; you build the schedule (AWX, AAP or CI) and protect the control node like a bastion
Thousands of long-lived hosts where a hand-fix must be reverted automatically and reportedPuppet (Core, PE, or OpenVox)Agent pulls a catalog every 30 minutes and reports every run; noop gives a safe audit modeA Puppet Server and CA to operate; a purchase from Perforce for production, or the OpenVox fork and its release cadence
An estate with existing cookbooks and Ruby fluency, moving to Policyfiles and Chef 19ChefPolicyfile locks give reproducible node policy; Agentless Mode covers the boxes you cannot install onChef Infra Server (Erlang, PostgreSQL, Bookshelf) to run and a Progress commercial licence for production
Regulated environment that wants continuous enforcement plus proof of the desired state per hostPuppet or ChefBoth report every run to a server you can query (PuppetDB, Chef Automate); Ansible reports only when invokedFleet-wide write access concentrates on the server; access to cookbooks or environments becomes a privileged path
Network devices, appliances and cloud APIs alongside serversAnsibleModules for targets that cannot run an agent or Python; network modules need no Python on the devicePlaybooks for devices are imperative in practice; idempotence depends on each module
Small team, no budget for a server tier, wants pull-based convergence anywayAnsible with ansible-pull, or OpenVoxansible-pull runs from cron against Git; OpenVox gives the Puppet model without a Perforce purchaseansible-pull is documented as rare and not recommended; OpenVox is a young fork whose compatibility promise you are relying on

Trade-offs by model

Push with no agent (Ansible)
Nothing to install or keep running on hosts, and the same tool does orchestration. In return there is no convergence between runs and a control node that holds every credential, so drift stays invisible until the next run and the control node becomes a tier-one asset. Teams without a scheduler in front of Ansible, and anyone whose compliance story is "we run it weekly", are carrying that risk today.
Pull on a schedule (Chef, Puppet)
Continuous enforcement and a report per run per node. The bill is an agent on every host, a server to operate, and fleet-wide write access concentrated wherever cookbooks or environments are merged. Incident response then needs an agreed way to pause the agent (noop, service stop) before hand edits, or the edits vanish within the interval; on-call engineers and whoever approves merges to the policy repository should both know that rule.
Licence as an operating constraint
The commercial paths come with support, SLAs, hardened builds and vendor-maintained releases, priced per node or per agreement before a single host is managed, and subject to a vendor decision (Perforce, Progress) that can change the terms again. That makes the choice between Chef, Puppet Core and OpenVox partly a procurement choice, which is why whoever signs and whoever would run the migration if the terms change should be in the same meeting.

Constraints first, then the tool

Settle three constraints before you compare features. Can the hosts reach a server, or must the server reach the hosts? Does a hand-fix have to be reverted automatically, or is that a change-management failure you want to see? Is there budget and appetite for a commercial agreement, and if not, is a community fork an acceptable dependency?

If the hosts cannot run an agent, or the same team needs orchestration and ad hoc runs, the answer is Ansible and the work is building the scheduler and hardening the control node. If enforcement between runs is a requirement, the answer is Puppet or Chef, and the decision between them is whether you are buying from Perforce or Progress, or betting on OpenVox, more than it is about DSL taste. All three paths are documented, current and supportable. Syntax is the last thing worth comparing; the operating model and the invoice arrive whether or not you looked at them first.

Found a technical issue on this page? Report it with the tool version you used and the behavior you saw. How resources are maintained.

Go deeper
Hands-on courses on Ansible & Chef & Puppet