1. What Ansible is, and why it exists
Ansible · 1. What Ansible is, and why it existsYou have forty servers. One of them needs a package installed, a config file written, and a service restarted.
That is a one-line shell command, forty times. You write a for loop over SSH and it works, and you feel briefly clever. Then reality arrives in the usual order:
host 17 was down when the loop ran. → it is now different from the other 39, and nothing anywhere records that.
the loop half-finished on host 23. → the package installed, the config did not
you run the loop again to fix host 17 → and it re-appends the config line to all 39
a new host joins → which loops has it had run against it? nobody knows
someone edits a file by hand at 2 a.m. → and it stays edited for two yearsEvery one of those is the same failure. A script describes an action. You need to describe a destination.
The idea
Section titled “The idea”Write down what the machine should look like. Run a tool that makes the machine look like that, and does nothing if it already does.
That is configuration management, and it is not a new idea — CFEngine had it in 1993, Puppet in 2005, Chef in 2009. What Ansible changed was the cost of adopting it.
THE OTHERS ANSIBLE
install an agent on every nothing on the target machine but Python and SSH
run a central server no server required (Puppet master, Chef server)
learn a custom DSL YAML
the agent PULLS every you PUSH when you 30 minutes decide to
bootstrap problem: how do none — if you can SSH you install the agent? to it, you can manage itThat last row is the one that won. Every pull-based system has a chicken-and-egg problem — something has to install and configure the agent — and it is usually solved with a shell script, which is the thing you were trying to escape. Ansible has no agent, so there is nothing to bootstrap. If SSH works, Ansible works.
Agentless is the entire pitch, and the cost of it is chapter 3, which is about what “no agent” really means when you have four thousand hosts.
Where it came from
Section titled “Where it came from”Michael DeHaan wrote Ansible in February 2012. He had previously built Cobbler (provisioning) and Func (remote execution) at Red Hat, and had watched both Puppet and Chef succeed while remaining, in his view, too heavy for the people who most needed them.
The name is taken from science fiction: an ansible is a device for instantaneous communication across any distance, coined by Ursula K. Le Guin in 1966 and borrowed widely since. It is a good name for a tool whose entire job is “do this, everywhere, now”.
Ansible Inc. was founded in 2013 and acquired by Red Hat in October 2015. It is now the default configuration management tool in most organisations that have one, and the reason is almost always the same: it was the one someone could adopt on a Tuesday afternoon without asking permission to install anything.
The version fact that confuses everyone
Section titled “The version fact that confuses everyone”There are two things called Ansible and you need to know which one you have.
ansible-core the engine. The binaries, the execution model, and about 70 built-in modules. pip install ansible-core
ansible the community package. ansible-core PLUS several hundred collections maintained by everyone else. pip install ansibleUntil Ansible 2.9 (October 2019) there was one monolithic release containing thousands of modules. In 2.10 the project split them out into collections, versioned and distributed independently, and the module you call is now written in full as a fully-qualified collection name:
- ansible.builtin.copy: # ships with ansible-core- community.general.pam_limits: # a separate collection- amazon.aws.ec2_instance: # another oneShort names like copy: still work for built-ins, and you will see them everywhere in older material. Write the FQCN anyway. It is unambiguous, it is what linting will demand, and it tells the next reader where the module comes from — which matters the first time two collections define something with the same short name.
The practical consequence: most tutorials written before 2020 are about a tool that no longer ships that way. If a module does not exist, the usual answer is that it lives in a collection you have not installed.
What Ansible actually does
Section titled “What Ansible actually does”The mechanism is simpler than people expect, and understanding it explains almost every behaviour in this guide.
For each task, for each host:
1. Take the module (a Python file) and the arguments 2. Build it into a self-contained payload 3. Open an SSH connection 4. Copy the payload to a temp directory on the target 5. Run it with the target's Python 6. The module prints ONE LINE OF JSON and exits 7. Read the JSON, delete the temp directory 8. Decide: ok / changed / failed / skippedThere is no daemon, no persistent state on the target, and nothing left behind. The “agent” exists for about 200 milliseconds and then deletes itself.
The JSON line is the whole interface. A module is any program that accepts arguments and prints JSON — which is why writing your own is unusually easy, and why the changed field in that JSON is the single most important concept in the tool.
Idempotence, and what it really means
Section titled “Idempotence, and what it really means”Running the same playbook twice should report changes the first time and none the second.
This is the property everything else rests on. It means a playbook is safe to run repeatedly, safe to run on a host that is already correct, and safe to run on a host that is half-configured because a previous run failed.
ansible.builtin.package: name=nginx state=present
run 1: nginx is missing → installs it → CHANGED run 2: nginx is present → does nothing → OK run 3: nginx is present → does nothing → OKIdempotence is a property of the module, not of Ansible. ansible.builtin.package checks before it acts. ansible.builtin.command: apt-get install nginx does not — it runs the command every time and reports changed every time, because it has no idea what the command means.
That distinction is chapter 5, and it is where most bad Ansible comes from.
The honest criticism, and the honest answer
Section titled “The honest criticism, and the honest answer”The Docker guide’s first chapter makes a pointed charge against tools like this one, and it is worth quoting rather than dodging:
“It is convergent, not reproducible: you describe a destination and hope the machine gets there. Run it on a fresh box and a box that has been alive for two years and you can get two different results, because the tool only changes what it knows to change.”
That criticism is correct. Ansible only manages what you told it to manage. A file you removed from your playbook last year is still on every machine that ever ran the old version. A package installed by hand stays installed. A configuration line added by someone debugging at 3 a.m. survives every future run, because no rule mentions it.
The word for that gap is drift, and configuration management reduces it without eliminating it. Only rebuilding from scratch eliminates it — which is what containers and immutable images do, and why they took over the parts of the industry where rebuilding is cheap.
So the honest position, which chapter 15 develops properly:
Can you throw the machine away and build a new one? → do that. Images, containers, autoscaling groups. Ansible's role shrinks to BUILDING the image.
Are you stuck with machines that must live for years? → databases on real hardware, network appliances, regulated environments, an office, a factory, someone else's datacentre → this is Ansible's job, and nothing else does it as wellAnsible is not competing with containers. It is what you use where containers are not on offer. And it is also, very often, the thing that builds the container image or the machine image in the first place.
The four jobs
Section titled “The four jobs”1. Configuration management. The original job. Packages, files, services, users, firewall rules — described once, applied everywhere, repeatedly.
2. Ad-hoc remote execution. One command, right now, on a set of machines.
ansible webservers -m ansible.builtin.command -a "uptime"ansible all -m ansible.builtin.package -a "name=curl state=latest" --becomeThis is genuinely useful and genuinely underused. It is ssh in a loop with proper host selection, parallelism and reporting.
3. Orchestration. Doing things across machines in an order: drain a server from the load balancer, upgrade it, run a health check, put it back, then move to the next one. Chapter 11 is entirely about this, and it is what separates Ansible from the tools it replaced — Puppet has no notion of “then”.
4. Provisioning, sort of. Ansible has modules for creating cloud resources. It works, and Terraform’s chapter 15 is right that it is worse at it — no plan, weaker state tracking, no dependency graph. Terraform creates the machine; Ansible configures it. The two guides agree on this from both sides.
What it is not
Section titled “What it is not”It is not fast. It opens SSH connections, copies files, and starts a Python interpreter per task per host. The default parallelism is five hosts at a time. Chapter 13 makes it much faster; nothing makes it fast in the way a compiled agent is fast.
It is not a scheduler. It runs when you run it, or when CI runs it. Nothing loops in the background enforcing state. If you want continuous enforcement, you schedule it yourself — or you use a pull-based tool, which is exactly the trade Puppet made.
It is not a programming language, although the temptation to use it as one is enormous. Chapters 7 and 8 are partly a warning about this: a playbook with deeply nested loops, recursive includes and thirty conditionals has become a program written in the worst possible language for programs.
It is not secure by virtue of being agentless. It runs as root on everything you own, from one machine, over SSH. Chapter 12 takes that seriously.
The mental model to carry
Section titled “The mental model to carry”1. INVENTORY says which machines exist and how they are grouped. Everything starts here.
2. A PLAYBOOK maps a group of hosts to a list of tasks.
3. A TASK calls a MODULE with arguments. The module is copied to the target, run, and deleted. It prints JSON saying what it did.
4. IDEMPOTENCE is the module's job, not Ansible's. `changed` means something actually happened.
5. VARIABLES come from about twenty places with a strict precedence order, and this is where most confusion lives.
6. Ansible PUSHES, in a strict order you control, with no agent and no state on the target.Point 5 is the one that generates the most support questions in the world, and it is chapter 6.
Why Ansible in 2026
Section titled “Why Ansible in 2026”The prediction that containers would make configuration management obsolete was half right. The half that came true is real: for stateless applications that can be rebuilt, images won, and they won completely.
The other half did not happen, because a large amount of the world’s computing does not consist of stateless applications that can be rebuilt. Database servers. Network devices. Physical hardware in factories and hospitals. Kubernetes clusters themselves, which run on machines that somebody has to configure. Regulated environments where the machine must be provably the same one that was audited. Air-gapped sites. Edge locations. The build step that produces the image everyone else deploys.
Ansible is the tool for machines that persist. That is a smaller share of the industry than it was in 2015, and it is not a small share, and it is not going away.
There is also a second reason to learn it that has nothing to do with servers. Ansible is the most widely-understood way to write down an operational procedure so that it can be reviewed, versioned, and run by someone who was not there when it was designed. A playbook is a runbook that executes. That is worth something even if you only ever use it on one machine.
What to take from this chapter
Section titled “What to take from this chapter”- A script describes an action; configuration management describes a destination. Every failure of the
for-loop-over-SSH approach is that one distinction. - Ansible’s winning move was agentless push over SSH — nothing to install on the target, no server to run, no bootstrap problem, and YAML instead of a custom language.
ansible-coreis the engine;ansibleis core plus several hundred collections. Since 2.10, modules live in collections and should be written as FQCNs likeansible.builtin.copy.- The mechanism: copy a self-contained Python payload to the target, run it, read one line of JSON, delete it. The agent lives for 200 milliseconds.
- Idempotence is the module’s property, not Ansible’s.
packagechecks first;commandcannot. - The criticism that configuration management is convergent, not reproducible, is correct. Drift is reduced, not eliminated. If you can rebuild the machine, rebuild it — and use Ansible to build the image.
- Four jobs: configuration, ad-hoc execution, orchestration (the one Puppet cannot do), and provisioning, which Terraform does better.
- It is not fast (five hosts at a time by default), not a scheduler, and not a programming language — though it will let you try.
- Ansible is the tool for machines that persist.
Next: inventory — where the list of machines comes from, why groups matter more than hosts, and the dynamic sources that mean you never write the list down at all.