Configuration management in 2026: revisit Ansible, Salt and Puppet before you write another server setup script
Almost every team has one: a setup.sh or bootstrap.sh written for the first production server and grown every year since. It installs packages, edits configuration files with sed, creates users and starts services. It works on a fresh machine. Nobody is sure what happens if you run it twice, or how the 40th server differs from the first. When the next server is needed, the easy path is to copy the script, change two lines and run it again.
Configuration management tools were built to replace this habit. Ansible, Salt and Puppet are each more than ten years old, and containers, Kubernetes and image builders have taken over a good part of their old work. Yet most companies still run plain Linux virtual machines: databases, brokers, build agents, bastion hosts and on-premises boxes at customer sites. Someone has to keep those machines in a known state. The tool landscape has also shifted under people’s feet: Puppet’s vendor moved its new builds behind commercial terms, the community answered with a fork called OpenVox, Salt now lives inside Broadcom, and Ansible keeps changing its core in every release.
This post compares the three main open source options as they stand on 27 October 2026: Ansible (we mean ansible-core and the community package), Salt, and Puppet together with its community fork OpenVox. We also mention Chef and its community build, Cinc, where it helps. We look at licences and ownership, push versus pull architecture, idempotency and drift, scale, upgrades, and how these tools fit with immutable images, Kubernetes and OpenTofu or Terraform. Then we work through a plan for one invented team.
A few earlier posts sit next to this one. In OpenTofu and Terraform: revisit the fork before you pick one we looked at the tools that create cloud resources; this post is about what happens inside a machine after it exists, and we will not repeat that comparison. In Vault and OpenBao: revisit your secrets manager we covered where secrets should live, which matters because configuration code is where secrets most often leak. In Argo CD and Flux: revisit your GitOps controller we looked at the pull model for Kubernetes, which is the same idea Puppet used for servers many years earlier. And secret scanning with Gitleaks, TruffleHog and detect-secrets is worth reading before you put your first inventory file into Git.
A scenario to keep us honest
Take an invented Guwahati company that builds warehouse, cold chain and dispatch software for tea estates, food distributors and pharma stockists across the North East. About 18 engineers, with a two-person platform team. The product runs partly in Kubernetes and partly on plain virtual machines.
Their server estate looks like this:
- About 140 Linux virtual machines: around 90 in a cloud region in Mumbai and around 50 in a colocation rack in Guwahati that serves customers with poor connectivity to the cloud. Most run Ubuntu LTS; a few older ones still run an older Ubuntu release that the team keeps meaning to retire.
- New services run on a Kubernetes cluster, deployed through Argo CD. Cloud networks, load balancers and managed databases are created with OpenTofu.
- About 60 of the virtual machines are managed by open source Puppet 6, set up in 2019 by an engineer who has since left. There is one Puppet server on a single VM, no PuppetDB, and a control repository with 30 modules, half of them copied from the Forge and edited by hand. The Puppet 6 line reached end of life years ago.
- The other 80 machines were built with a 1,900-line
bootstrap.shand a few Ansible playbooks that newer engineers wrote for deployments. The playbooks are run from laptops. Two engineers have slightly different copies. - Secrets are spread across Hiera YAML files in the Puppet repository, an Ansible vault file whose password is in a shared note, and environment files on the servers.
Two things set off the current discussion. First, a large pharma distributor asked for evidence, as part of a vendor audit, that all servers that touch their data follow a written hardening baseline, with patch status and SSH settings, and that the team can detect when a server drifts from that baseline. The honest answer was that the team could show this for some servers and not others. Second, a warehouse release failed one night because an engineer had fixed a problem on one colocation server by hand, changing an sshd setting and a kernel parameter, and the next run of bootstrap.sh on a fresh replacement machine did not include either change. The replacement behaved differently from the server it replaced, and it took four hours to find why.
After the meeting, the proposals came quickly. One engineer said “move everything to Ansible, no agents, everyone already knows YAML”. The engineer who had touched the Puppet code most said “upgrade Puppet, it is the only one of these that actually enforces state every 30 minutes”. A third engineer, who had used Salt in a previous job, said “Salt gives you both, plus an event bus, and it is fast”. The finance head asked whether Puppet now needed a paid licence.
The question this post tries to answer: what should this team use to keep 140 virtual machines in a known state, how should that fit with images, Kubernetes and OpenTofu, and what must change apart from the tool?
The short version
- Ansible (GPLv3 or later, sponsored by Red Hat) is agentless. A control node connects to machines over SSH (or WinRM and PowerShell remoting for Windows), copies small modules, runs them and removes them. The current core release is ansible-core 2.21.5 (5 October 2026), and the community package that bundles collections is Ansible 14.5.0 (6 October 2026). It is a push tool by default, so drift is only fixed when someone or something runs the playbook again.
- Salt (Apache 2.0, maintained by a core team employed by Broadcom) uses a master and minions connected over a persistent, encrypted message bus. It can also work agentless through
salt-sshand masterless throughsalt-call --local. The current long-term support line is Salt 3008 LTS, released on 27 May 2026; the latest point release, 3008.5, was published on 27 October 2026 (IST). Salt 3006 LTS is in security-fix-only support until June 2027. - Puppet is the oldest of the three and uses an agent that pulls a compiled catalogue from a server, by default every 30 minutes, and corrects drift on each run. Since early 2025 Perforce ships new Puppet builds, now called Puppet Core, from a private repository under a commercial licence, with free developer use up to 25 nodes. The source remains under Apache 2.0. The current Puppet Core releases are 9.1.0 and 8.22.0 (announced 22 September 2026).
- OpenVox is the community fork of Puppet run by Vox Pupuli under Apache 2.0, with public packages. OpenVox 9.0.0 arrived on 2 October 2026 and the latest release is 9.2.1 (23 October 2026). The OpenVox 8 line is still maintained, with 8.31.1 on 21 October 2026.
- Chef Infra (Progress) and its community build Cinc use a similar agent and pull model with a Ruby language. They are still in use, but we do not suggest starting a new estate on them unless your team already knows Chef well.
If you remember one thing: the tool matters less than the decision about which machines are long-lived and must be kept in a known state, and which should be rebuilt from an image instead. Make that decision first.
Where things stand on 27 October 2026
Ansible. ansible-core ships a major version about every six months (May and November) and patch releases every four weeks, maintaining three majors at a time. Per the official support matrix, 2.21 went GA in May 2026 and reaches end of life in November 2027; 2.20 ends in May 2027 and 2.19 in November 2026. The 2.20 and 2.21 control node needs Python 3.12 to 3.14; managed nodes need Python 3.9 or later. In 2.21 the paramiko connection plugin is removed and apt_key and apt_repository are deprecated in favour of deb822_repository. The 2.19 release reworked templating, and playbooks that relied on loose type handling started failing. Ansible 15, on ansible-core 2.22, is in development.
Salt. Salt 3008.0 was the first new LTS since 3006 in April 2023, and it arrived about a year later than the project had first estimated. It brings a new idea called Salt Resources, which lets one minion manage many remote things such as SSH hosts or appliances and target them like minions. It also adds optional OpenTelemetry tracing and metrics, tunable worker pools on the master, a memory-mapped cache backend for large key stores, and a packaged Python of 3.14. Many community modules, including most cloud provider modules, were moved out of the core into separately maintained Salt extensions during what the project calls the Great Module Migration. Salt packages moved from repo.saltproject.io to packages.broadcom.com in October 2024, and packages older than 3006 are no longer available there. The support lifecycle page lists 3008 LTS with active support until June 2027 and security support until June 2028, and 3006 LTS with security support until June 2027.
Puppet. On 7 November 2024 Puppet (a Perforce business unit) announced that from early 2025 new binaries and packages built by its team would go to a private, hardened repository. Community developers get free access under a developer licence for up to 25 nodes; above that a commercial licence is needed. The source code stays under Apache 2.0, and the Forge stays open. The product is now called Puppet Core. The Puppet Core release notes say Puppet Core 8 and 9 are developed and supported as a proprietary product, with projected end of life in February 2028 for 8.x and February 2029 for 9.x. Open source Puppet 7 reached end of life in April 2025. The public puppetlabs/puppet repository still exists but receives far fewer commits than before.
OpenVox. Soon after the Perforce announcement, the Vox Pupuli community, which already maintained a large share of popular Puppet modules, started OpenVox: a fork of the agent, server, PuppetDB and Facter (as OpenFact), with packages at apt.voxpupuli.org and yum.voxpupuli.org. It aims to stay compatible with Puppet code. OpenVox 9.0.0 moves the agent to Ruby 4.0 and the server to JRuby 10.1, needs Java 21 or 25 on server hosts, and changes some defaults that matter in practice. The upgrade notes say agents no longer fall back to the server name puppet when server is not set, and the default for reports changed from store to none.
Chef and Cinc. Chef Infra source code is under Apache 2.0, but since 2019 the official Chef binaries come under a commercial licence. Cinc is a community project that builds and distributes the same source without Chef trademarks, under the open licence. The Cinc stable channel listed Cinc Client 19.4.46 when we checked.
First question: why do you want to change?
Before comparing tools, write down why this discussion started. In the Guwahati case, neither trigger was really about the tool.
- The audit asked for a written baseline, evidence that machines match it, and a way to notice drift. Any of the three tools can produce that, but only if the baseline exists and all machines are under management. Today 80 machines are not.
- The failed release was caused by a hand fix that never went back into code. A tool does not stop that by itself. A team rule does: a hand fix on a server is an incident, and the incident is closed only when the fix is in the repository.
- The Puppet 6 installation is unsupported, which is a real risk, but it is an upgrade problem first and a tool choice second.
So the first decision is not Ansible versus Puppet. It is: which machines should be kept alive and corrected, which should be rebuilt from an image, and who owns the code that describes them.
What each tool is
Ansible
Ansible was created by Michael DeHaan and first released in 2012. Red Hat acquired the company behind it in 2015. You write playbooks in YAML; each task calls a module, such as ansible.builtin.copy, ansible.builtin.template, ansible.builtin.package or ansible.builtin.service. Modules are grouped into collections. ansible-core contains the engine and the built-in modules; the community ansible package adds a large curated set of collections on top.
A control node (a laptop, a CI runner or a small VM) reads an inventory, connects to machines in parallel and runs tasks in order. There is no agent and no open port other than SSH. Because tasks run in the order written, Ansible feels like a well-behaved script; that is its strength and its trap. For schedules and a web interface, teams use AWX (the open source upstream of Red Hat Ansible Automation Platform) or a CI pipeline, and ansible-pull gives a pull mode on the machine itself.
Salt
Salt was started by Thomas Hatch in 2011. SaltStack, the company, was acquired by VMware in 2020, and Broadcom completed its acquisition of VMware in November 2023. The commercial product is now sold as part of the VMware portfolio, and the open source project is maintained by Broadcom employees with community working groups.
A Salt master publishes jobs on one port and receives results on another; each minion keeps a persistent connection and authenticates with a key the master accepts, so a command reaches thousands of minions in seconds. Configuration is written as states in YAML and Jinja (SLS files), with grains (facts about the machine) and pillar (data the master gives to chosen minions). An event bus carries every job and result; a reactor on the master can respond to events, and beacons on minions can watch files or services and emit events.
Salt also runs agentless over SSH with salt-ssh, and masterless with salt-call --local, which is useful inside image builds.
Puppet and OpenVox
Puppet was created by Luke Kanies, first released in 2005, and its company was acquired by Perforce in 2022. You write manifests in the Puppet language, a declarative language where you describe resources (a package, a file, a service, a user) and their relationships. Facter collects facts on each node; Hiera supplies data by hierarchy (common, then environment, then role, then node).
Each node runs an agent. The agent sends its facts to the Puppet server, the server compiles a catalogue for that node, the agent applies the catalogue and sends back a report. By default this happens every 30 minutes. If someone changes a managed file by hand, the next run puts it back and records the change in the report. PuppetDB stores facts, catalogues and reports, and lets one node’s configuration depend on another’s, for example a load balancer that collects the list of its backends.
OpenVox runs the same model and the same language, with package names such as openvox-agent, openvox-server and openvoxdb. For a team, the practical difference is licence and supply: OpenVox packages are public and free to use at any scale, while Puppet Core packages above 25 nodes need a commercial agreement with Perforce and come with vendor support.
Side by side
Licence and ownership
- Ansible:
ansible-coreis GPLv3 or later. Red Hat sponsors development and sells Ansible Automation Platform, which bundles supported content, a controller and other tools. The community package and AWX are free. The GPL applies to the tool, not to your playbooks; your playbooks are your own code. - Salt: Apache 2.0, owned by Broadcom. The source and packages are public. The risk to think about is not the licence but the owner’s priorities: the Salt team has said openly that it focuses on the core and has moved cloud modules out to community extensions.
- Puppet: source under Apache 2.0, but new official builds come from a private repository under a commercial EULA, free for development use up to 25 nodes. Read the agreement yourself; it restricts use to internal business purposes and limits node counts by licence.
- OpenVox: Apache 2.0 source and public packages, run by a volunteer community with commercial supporters. You get openness, and you take on the usual community risk: release speed and platform coverage depend on people’s time.
- Chef and Cinc: Apache 2.0 source; Chef binaries under commercial terms; Cinc binaries free.
For the Guwahati team, 60 managed nodes today and 140 soon means Puppet Core would need a paid licence. That is a business decision, not a technical flaw, and some companies will happily pay for an SLA on CVE fixes. But the team should not drift into a licence by accident by pointing machines at a new repository.
Architecture: push, pull and the event bus
This is the most important difference, and it decides how drift is handled.
- Ansible pushes. Nothing happens on a machine unless a control node runs a playbook against it. That is simple and easy to reason about. It also means a machine can drift for weeks if nobody runs the playbook. You can add a schedule (AWX, a CI job, or
ansible-pullwith a timer), but then you are building your own pull system and must also handle reporting. - Puppet and OpenVox pull. The agent asks for its catalogue on a schedule and enforces it. Drift is corrected without anyone remembering to run anything, and every run leaves a report. The cost is a server to run, certificates to manage, and the discipline to keep the code correct, because a bad change reaches every node within half an hour.
- Salt does both. The master can push states on demand to any target in seconds, and minions can apply states on a schedule. The event bus and reactor add a third mode: respond to things that happen. The cost is two open ports on the master, key management for minions, and a central component that must be patched quickly.
A simple way to think about it: push suits changes that a person starts (a deployment, a rotation, a one-off fix across many machines). Pull suits rules that must always be true (SSH settings, users, packages, kernel parameters, log shipping). Many teams end up with one of each.
Language and learning curve
- Ansible YAML is the easiest to start with and the easiest to misuse; Jinja logic inside YAML gets hard to read quickly. Roles, collections and
ansible-linthelp. - Salt states are also YAML and Jinja, with richer requisites (
require,watch,onchanges,prereq) and powerful pillar and grains targeting. More moving parts means a steeper start. - The Puppet language is declarative: resources are ordered by relationships, not line order, which surprises new users. Once learned, it states “this is how the machine should be” very well, with Hiera keeping data out of code.
Idempotency and drift
All three tools offer idempotent modules or resources: you describe the end state, and the tool changes only what differs. The catch is that every tool also lets you run arbitrary commands (shell and command in Ansible, cmd.run in Salt, exec in Puppet). Those are idempotent only if you make them so with guards such as creates, unless or onlyif.
How each tool reports a difference:
- Ansible reports each task as
ok,changed,skippedorfailed.--checkruns in dry-run mode, and--diffshows what would change in files. Not every module supports check mode, and plainshelltasks are skipped in check mode, so a dry run can hide what a real run would do. Our small drill below showed exactly this. - Salt supports
test=Trueon state runs, which reports what would change, and returns a structured result per state. - Puppet supports
--noopper run or per resource, and every agent run reports changed, failed, skipped and corrective changes. With PuppetDB, you can ask which nodes changed and why.
Drift detection, the thing the auditor asked for, is strongest with Puppet or OpenVox because it is built in. With Ansible you get it by running in check mode on a schedule and collecting results. With Salt you can run scheduled state.apply test=True and send results to a returner.
Scale and performance
All three tools are used on fleets of thousands of machines. The bottlenecks are different:
- Ansible is limited by the control node and SSH. The default
forksvalue is small and needs tuning; SSH pipelining helps. Much of a large run is connection setup and Python start-up on each target. - Salt sends commands to many machines quickly because connections stay open. Master workers, key store and job cache are the pressure points, which is why 3008 added worker pools and a memory-mapped key cache. Any speed claims for these are the project’s own; we did not measure them.
- Puppet and OpenVox are limited by catalogue compilation on the JVM-based server. Larger fleets add compile servers behind a load balancer and a separate PuppetDB with PostgreSQL.
We did not run any benchmark for this post. Vendors and community members publish numbers for each tool, but they are measured on their own setups; read them as the vendor’s own run, not as a number your fleet will see. For 140 machines, any of the three is fast enough. The decision should be about the model, not speed.
Secrets
Ansible has Ansible Vault and lookup plugins; Salt has GPG-encrypted pillar; Puppet has hiera-eyaml and deferred functions, so a secret is fetched on the agent instead of being compiled into the catalogue. For a growing team, an external secrets manager such as Vault or OpenBao is the better pattern in all three. Run a secret scanner on the configuration repository before and after any move; old Hiera files and shared vault passwords are a common leak.
Upgrades and operations
- Ansible: upgrades mean the control node and collections, not the managed machines, as long as they have a supported Python. That makes upgrades cheap but frequent: three supported major versions at a time, a new one every six months, and porting guides to read. Pin
ansible-coreand collections in a requirements file and test playbooks in CI before you move. - Salt: upgrade the master first, then minions; the master should not be older than its minions. 3006 to 3008 is a big jump: packaged Python changed, some modules moved to extensions you must install separately, and some defaults changed (the
x509_v2modules are now the default). - Puppet and OpenVox: you upgrade the server and PuppetDB first, as a set, then agents. Major versions bring language and Ruby changes. Puppet 8 made strict mode the default and removed legacy facts from the default output, which breaks old modules that still use names like
$::operatingsystem. OpenVox 9 adds Ruby 4.0, new Java requirements and the default changes already mentioned. Agents on the previous major can usually talk to a newer server for a while, but check the specific compatibility notes for your versions.
Security matters most for central servers. Internet-exposed Salt masters were attacked at scale in 2020 after critical authentication bypass flaws were published. A configuration server can change every machine you have: keep it on a private network, patch it quickly, and restrict who can push code to it.
How it fits with images, Kubernetes and OpenTofu
This is where 2026 is different from 2015.
- Immutable images. If a machine can be rebuilt from an image in minutes, it is often better to build a new image than to correct the running machine. Configuration management still has a job here: it runs once, at image build time, inside a tool like Packer, and makes the image reproducible. Ansible is popular for this because it needs no agent; Salt masterless and OpenVox with
puppet applywork too. - Kubernetes. Inside a cluster, the desired state lives in manifests and is enforced by controllers such as Argo CD or Flux. Do not use Ansible, Salt or Puppet to manage applications inside Kubernetes. They still have a role on the nodes if you run your own nodes on plain Linux, though lightweight Kubernetes distributions and immutable node operating systems reduce that need.
- OpenTofu and Terraform. These create the machine, the network and the managed services. Configuration management configures what is inside the machine. Keep the boundary clear: OpenTofu outputs an inventory or tags, and the configuration tool reads them. Avoid running configuration tools from inside OpenTofu provisioners for long-lived machines, because a failure leaves state that neither tool fully owns.
What actually happens when you move from Puppet 6 to something new
- Staying on Puppet code (OpenVox 8 or Puppet Core 8). The Puppet 6 to 8 jump is two major versions. Modules that use legacy facts, old functions or Ruby written for older Ruby need fixing, and hand-edited Forge modules must be compared with upstream and either reset or recorded as forks. The model stays the same and the team keeps its 30 modules.
- Rewriting in Ansible. Every Puppet class becomes a role, relationships become task order, and the 30-minute enforcement goes away unless you add a schedule. For 60 machines this is weeks of work, not days.
- Rewriting in Salt. Similar effort, plus a master to run and secure, with one engineer as the only person who knows it.
Whatever path you pick, do not convert all 60 machines at once. Move role by role, starting with the simplest.
A worked plan for the Guwahati team
Step 1: Classify every machine
List all 140 machines with their role, owner and how they were built. Then mark each one as one of three types:
- Rebuildable: can be replaced from an image without losing data (web front ends, workers, build agents).
- Long-lived: holds data or state that makes rebuilding costly (database servers, message brokers, the colocation file servers).
- Retire: no clear owner or purpose. The older Ubuntu machines go here first.
In our invented case this gave roughly 70 rebuildable, 55 long-lived and 15 to retire. The exact numbers do not matter; the classification does.
Step 2: Freeze the setup script and set the hand-fix rule
No new lines go into bootstrap.sh. Every hand change on a server must be followed, the same day, by a change in the repository. Add this to the incident template. Without this rule, no tool will fix the problem that broke the warehouse release.
Step 3: Write the baseline before choosing the tool
Write the hardening baseline the auditor asked for as a short document: SSH settings, users and keys, time sync, log shipping, automatic security updates policy, kernel parameters, firewall defaults, agent for monitoring. Base it on a published benchmark that suits your distribution, but keep only what you will actually enforce. This document becomes the first role in whatever tool you pick.
Step 4: Pick a model for each type of machine
- Rebuildable machines: build images with Packer and run Ansible inside the build, once. No agent on the running machine. Replace, do not patch, for anything beyond security updates.
- Long-lived machines: use pull-based enforcement so drift is corrected and reported without anyone remembering. For this team, that means OpenVox 8 for the 60 Puppet machines, after a careful upgrade, and bringing the 55 long-lived machines under it over time.
- Changes that people start (deployments, certificate rotation, a one-off fix across all machines): Ansible from CI, never from laptops.
The team chose OpenVox over Puppet Core because they want public packages and no node limit, and do not need a vendor SLA today. A company that needs contracted CVE response times should consider Puppet Core seriously. They did not add Salt: its event bus suits teams that want automatic responses at scale, but only one person here knows it, and the audit need is drift detection, which OpenVox already gives.
Step 5: Upgrade the Puppet estate in a test environment first
Set up a new OpenVox 8 server and PuppetDB on a fresh VM with a new CA, next to the old Puppet 6 server. Point two non-critical machines at it and fix modules until runs are clean: replace legacy facts, update Forge modules, and reset or formally fork hand-edited ones. Then move machines role by role. Keep the old server read-only for a month, then shut it down.
Plan the later step to OpenVox 9 separately, after reading its upgrade notes: set server explicitly in every agent’s puppet.conf, decide where reports go now that the default is none, check Java on the server, and reinstall any gems in the agent’s Ruby.
Step 6: Move Ansible off laptops
Put the playbooks in one repository with pinned ansible-core and collections, ansible-lint in CI, and runs from a CI runner with a dedicated SSH key. Replace the shared vault password with Vault or OpenBao. Fix any use of the removed paramiko connection or deprecated apt_key before moving to 2.21.
Step 7: Make drift visible
With OpenVox and PuppetDB, add a weekly report of nodes with corrective changes and failed runs, and send it to the platform team’s channel. For machines managed by Ansible only during image build, drift is handled by replacement: if a rebuildable machine is older than a set number of days, replace it. Give the auditor the baseline document, the report and the repository history.
Step 8: Retire the 15 machines and the script
Once every machine is either image-built or under OpenVox, archive bootstrap.sh with a note saying where each part now lives. Do not delete it on day one; people will want to look up what it used to do.
When Ansible alone is the better choice
There are many teams where the answer is simply Ansible, with no agent at all:
- Almost all machines are rebuildable and replaced often.
- The fleet is small, say tens of machines, and a nightly check-mode run with a report is enough drift detection.
- Network devices, appliances and Windows hosts are a large part of the estate; Ansible’s collection coverage for these is very wide.
- The team does not want to run any central server, and is happy to run playbooks from CI on a schedule.
When Salt is the better choice
- You need to run commands on thousands of machines and get results in seconds, for example during incident response.
- You want event-driven automation on the servers themselves: restart, re-apply or alert based on beacons and the reactor.
- You already run Salt well, have patched masters and a team that knows pillar and requisites. Moving away from a working Salt estate rarely pays off.
Check the Broadcom-era changes carefully: cloud modules are no longer maintained by the core team, so if you used Salt Cloud or cloud states heavily, plan to use OpenTofu for that part.
When not to change at all
- Your Puppet or Salt estate is on a supported version, runs cleanly and has an owner. A working pull system is valuable; replacing it for fashion costs months.
- Your machines are mostly managed by Kubernetes and images already, and the remaining few are covered by a short Ansible playbook. Do not add an agent fleet for ten machines.
- The real problem is ownership or discipline (hand fixes, no reviews). Fix that first; the tool will not.
A short history, so the choices make sense
- 1993: CFEngine, by Mark Burgess, starts the idea of declaring desired state and converging machines towards it.
- 2005: Puppet is released, with a declarative language and an agent that pulls a catalogue.
- 2009: Chef arrives, with a Ruby language and a similar pull model.
- 2011: Salt is released, built around a fast message bus for remote execution, with states on top.
- 2012: Ansible is released, agentless over SSH, with YAML playbooks.
- 2019: Chef keeps its source under Apache 2.0 but moves its official binaries to commercial terms; Cinc starts as the community build.
- 2020: Ansible splits into
ansible-base(lateransible-core) and collections. VMware acquires SaltStack. Progress acquires Chef. Critical flaws in Salt masters lead to widespread attacks on exposed servers. - 2022: Perforce acquires Puppet.
- 2023: Broadcom completes its acquisition of VMware in November. Salt 3006 becomes the first LTS release under the new LTS and STS policy.
- 2024: Salt packages move to
packages.broadcom.comin October. On 7 November, Puppet announces that new builds will go to a private repository from early 2025. - 2025: Puppet Core arrives; open source Puppet 7 reaches end of life in April. ansible-core 2.19 reworks templating in July.
- 2026: ansible-core 2.21 in May; Salt 3008 LTS on 27 May; Puppet Core 9.1.0 announced on 22 September; OpenVox 9.0.0 on 2 October, built on Ruby 4.0.
The Vox Pupuli community started OpenVox soon after the Perforce announcement; we could not confirm the exact date of its first release, so it is not in the list.
Operational costs to plan for
- People. The largest cost by far. Someone must own the configuration repository, review changes, and keep versions current. For a 140-machine estate, plan for a meaningful part of one engineer’s time every month, more during migration.
- Servers. Ansible needs a CI runner or a small AWX installation. OpenVox or Puppet needs a server, PuppetDB and PostgreSQL, sized for the fleet and backed up. Salt needs a master, ideally two, and a job cache.
- Licences. Zero for Ansible community, Salt open source, OpenVox and Cinc. Puppet Core above 25 nodes, Ansible Automation Platform and Chef binaries need commercial agreements; get quotes before deciding, not after.
- Upgrades. Ansible every six months on the control node and collections; Salt once a year for LTS; Puppet or OpenVox when a major line ends, with Ruby and Java changes to test.
- Testing. A CI pipeline that applies your roles to throwaway VMs or containers (for example Molecule for Ansible) saves more incidents than any other investment here.
- Security. Patch central servers fast, rotate keys and certificates, review who can push code, and scan the repository for secrets.
A decision guide
- Mostly rebuildable machines and a small team: Ansible for image builds and changes, plus replacement instead of drift correction.
- Many long-lived machines that must stay compliant, and an audit asking for drift evidence: OpenVox (or Puppet Core if you want a vendor SLA and are ready to pay).
- Very large fleets, fast remote execution and event-driven responses: Salt, if you can run and secure masters well.
- An existing working estate on any of these: stay and upgrade, unless there is a clear reason to move.
- An existing Chef estate: consider Cinc for free binaries.
- Applications on Kubernetes: none of these; use GitOps controllers.
- Cloud resources: OpenTofu or Terraform, with configuration management only inside machines.
Checklist before you write another server setup script
- Every machine is listed with owner, role and type (rebuildable, long-lived, retire).
- A written baseline exists and is short enough to read in ten minutes.
- New machines are built from images or code, never by copying a script.
- Hand fixes on servers go into the repository the same day.
- Tool versions and collections or modules are pinned and upgraded on a calendar.
- No
shell,cmd.runorexecwithout a guard that makes it idempotent. - Dry runs (
--check --diff,test=True,--noop) are used before risky changes, and you know which tasks they skip. - Drift is reported somewhere a human reads every week.
- Secrets come from a secrets manager, not from files in the repository.
- The configuration server, if any, is on a private network and patched within days of a security release.
- The licence terms for every package repository you point machines at are read and recorded.
Common mistakes
- Writing shell scripts in YAML. A playbook full of
shelltasks is the oldbootstrap.shwith extra steps. Use modules. - Trusting check mode blindly. Tasks that do not support check mode are skipped and can hide real changes.
- Running playbooks from laptops. Different versions, different variables, no record.
- Copying Forge or Galaxy content and editing it in place. You lose the upgrade path. Wrap or fork it properly.
- Using the configuration tool to manage Kubernetes applications. Two systems then fight over the same state.
- Leaving the Puppet or Salt server reachable from the internet. It controls every machine.
- Assuming the old default. OpenVox 9 changed defaults for
serverandreports; Salt 3008 moved modules out of core; ansible-core 2.21 removedparamiko. Read release notes before every major upgrade. - Pointing machines at a new vendor repository without reading the licence. Changes like the Puppet Core move are easy to miss in a routine package update.
A small hands-on drill
We ran a small local drill on 27 October 2026, between about 10:21 PM and 10:24 PM IST, on a Linux x86_64 test machine, as a normal user, in a throwaway folder. We installed ansible-core 2.21.5 with pip in a Python 3.13 virtual environment and ran a five-task playbook against localhost with ansible_connection=local. The playbook wrote only into the drill folder. No SSH, no server, no agent and no system files were involved. We did not run Salt, Puppet, OpenVox or Cinc, and we did not measure performance.
The playbook created a directory, wrote a config file with listen_port = 8080, ensured one line with lineinfile, appended to a log with an unguarded shell task, and wrote a marker file with a shell task guarded by creates.
What we saw:
- First run:
ok=5 changed=5, as expected on an empty folder. - Second run:
ok=5 changed=1. The directory, the config file, the line and the guarded shell task all reportedok. Only the unguardedshelltask reportedchanged, because it appends every time. After three real runs the log file had three lines. - Drift: we changed the port in the config file by hand to 9090, the way an engineer might during an incident.
- Dry run with
--check --diff:ok=4 changed=1 skipped=1. The diff showed the hand change clearly (-listen_port = 9090,+listen_port = 8080) and the file was not touched. The unguardedshelltask was skipped in check mode, so the dry run said nothing about it, even though a real run would change something. - Real run:
ok=5 changed=2. The config file went back to 8080, and the unguarded task appended again.
This is a tiny drill, not a test of any tool at scale. It does show three things the post argues: modules give you idempotency for free, raw commands do not, and dry runs are only as honest as the tasks inside them.
Unlearn, re-learn
Unlearn the idea that a server setup script is a harmless shortcut, and the idea that picking the “best” configuration tool will fix drift. Unlearn, too, the old assumption that every machine must be kept alive and corrected forever. Re-learn configuration management as a small, owned system: a list of machines and their type, a short baseline, one repository with reviews, images for what can be rebuilt, pull enforcement for what must stay, push for changes people start, and release notes read before every upgrade. Practise it on the next machine you build, and apply the hand-fix rule from tomorrow.
Sources
- Ansible: ansible-core on PyPI (2.21.5 release, licence GPL-3.0-or-later), Ansible community package on PyPI (14.5.0), Releases and maintenance, including the ansible-core support matrix, ansible-core 2.21 changelog, ansible-core 2.19 changelog, ansible/ansible repository
- Salt: Salt 3008.0 release notes, Salt version support lifecycle, GitHub releases (3008.5, 3007.17, 3006.30), Package repository migration, FAQs after the repository migration, Open Hour notes, April 2025
- Puppet: Our Plans for Open Source Puppet in 2025 (7 November 2024), Puppet Core release notes and lifecycle, Puppet Core Software License Agreement, Puppet Core 9.1 and 8.22 announcement
- OpenVox: OpenVox GitHub releases (9.0.0 upgrade notes, 9.2.1, 8.31.1), OpenVoxProject on GitHub, Vox Pupuli
- Chef and Cinc: Chef Infra repository, Cinc project
- Drill: our own run, 27 October 2026, notes kept with the post
Releted Posts
Self-hosted Git forges in 2026: revisit Gitea, Forgejo and GitLab CE before you move another repository
Picture a software company in Kochi that builds billing and export-documentation software for seafood exporters in Aroor, Alappuzha, Kollam and Thoothukudi (an invented example, not a real client).
Read moreContainer registries in 2026: revisit Harbor, Zot and CNCF Distribution before you push another image
Picture a product engineering team in Bengaluru that runs about forty microservices on Kubernetes for a B2B payments product used by NBFCs across Karnataka and Maharashtra (an invented example, not a real client).
Read moreFeature flags in 2026: revisit Unleash, Flagsmith and GrowthBook before you hard-code another release toggle
Picture a test-preparation and online classes platform in Jaipur (an invented example, not a real client). Students use an Android app, an iOS app and a React web app.
Read more