Installing a package on one Linux server takes less than a minute. Installing it correctly on fifty servers every month is a completely different problem.
As infrastructure grows, the work itself rarely becomes more difficult. What becomes difficult is performing the same task consistently across dozens or hundreds of machines without forgetting a step or introducing configuration differences. One server ends up running an older package version, another misses a firewall rule, and someone eventually spends hours debugging an issue caused by a manual change nobody documented.
Ansible was built to solve exactly that problem. Instead of logging into every server and repeating the same commands, you describe the state you want, and Ansible brings every machine to that state automatically.
This article explains the concepts behind Ansible before introducing the syntax. Once you understand these ideas, reading production playbooks and building your own automation becomes much easier.
What is Ansible, and why does it exist?
The problem with managing servers manually
Imagine your team manages a web application running on ten Linux servers. A security update is released, and every server needs the same package upgrade. You connect to the first server, install the update, and restart the service. Then you repeat the process on the second server, then the third.
Halfway through, someone interrupts you.
When you return, you are no longer sure which machines have already been updated. One server misses the change, another receives the wrong configuration file, and over time every machine starts behaving a little differently. Production still works, but your infrastructure is no longer consistent.
Those differences are called configuration drift, and they become increasingly difficult to manage as environments grow. The challenge is rarely the command itself. The challenge is executing the same change accurately on every server, every time.
| Manual Administration | With Ansible |
| SSH into every server individually | Run one playbook against all servers |
| It is easy to forget one server or apply slightly different changes | Every targeted server receives the same changes |
| Configuration differences gradually appear between servers | Servers remain consistent |
| Tracking who changed what is difficult | Playbooks are stored in Git, making changes easy to review, audit, and roll back |
| Repeating routine tasks consumes time and increases the chance of mistakes. | Automation is repeatable, predictable, and can be executed safely whenever needed. |
Why shell scripts eventually stop scaling
Most engineers solve this problem with shell scripts. Instead of typing commands repeatedly, they automate the process with Bash.
That works well for small environments. Then another engineer adds logging, someone else adds error handling, and the script starts supporting both Ubuntu and Red Hat. A few months later, what started as a twenty-line script has become hundreds of lines that nobody wants to modify.
The limitation is not Bash itself. Shell scripts describe how to perform every step. Ansible focuses on what the final system should look like, leaving the implementation details to its modules. That difference makes playbooks easier to read, maintain, and share across teams.
What configuration management actually means
Configuration management is the practice of keeping every server in a known, predictable state. Instead of relying on documentation or memory, the desired configuration is written as code and applied consistently across the infrastructure.
For example, instead of saying:
Install Nginx.
You describe the desired outcome:
- Nginx should be installed.
- The service should be running.
- It should start automatically after every reboot.
If a server already matches that description, Ansible does nothing. If something is missing or configured incorrectly, Ansible changes only what is necessary. This behaviour, known as the desired state model, is the foundation of modern infrastructure automation.
Why Ansible became popular
Configuration management tools existed long before Ansible. Tools such as Puppet and Chef solved similar problems but required agents to run continuously on every managed machine.
Ansible took a simpler approach. It communicates over SSH, which Linux administrators already use every day. There is no permanent agent to install, automation is written in readable YAML, and getting started requires very little setup.
That simplicity made Ansible one of the most widely adopted automation tools in modern DevOps. Today, engineering teams use it to configure servers, deploy applications, install Kubernetes worker nodes, rotate certificates, automate operating system updates, and manage thousands of machines with the same playbook.
Before you write any Ansible
What you need installed
Every Ansible environment has two parts. The control node is the machine where Ansible runs. The managed nodes are the servers Ansible connects to and configures. Communication happens over SSH.
In most cases, the managed machines only need SSH access, Python installed, and a user with appropriate permissions. Unlike some configuration management tools, Ansible does not require a permanent agent running on every server. That makes it easier to introduce into existing environments without changing every machine first.
How Ansible communicates with servers
At a high level, every execution follows the same flow.
The control node reads your automation, connects to each server, executes only the required operations, and reports the result. Nothing happens manually once the playbook starts.
What an Ansible project actually looks like
Most Ansible projects follow a structure similar to this.
Each directory has a specific responsibility. inventory lists the servers, playbook.yml describes the automation, group_vars stores variables shared by groups, host_vars stores machine-specific values, and roles organize reusable automation. Keeping projects organized becomes increasingly important as your infrastructure grows.
The core building blocks
Every Ansible project is built from a few simple concepts. Once you understand how they fit together, most production playbooks become much easier to read.
Think of this as the execution pipeline Ansible follows every time you run a playbook.
Inventory: the machines Ansible manages
Ansible needs to know where your infrastructure lives. That information is stored in an inventory. A simple inventory might look like this.
Instead of targeting individual servers, you target logical groups such as web, database, or monitoring. As environments grow, inventories often represent staging, production, Kubernetes clusters, or multiple cloud regions. The inventory becomes the map of your infrastructure.
Modules: how Ansible performs work
Modules are the building blocks that perform individual operations. Need to install software? Use the package module. Need to create a user? Use the user module. Need to manage a service? Use the service module. For example:
Notice that you never tell Ansible how to install the package. You simply describe the desired result. The module handles the operating system details for you.
Tasks: one action at a time
A task is a single call to a module.
Every task should have one clear responsibility. Installing packages is one task, copying configuration files is another, and restarting services becomes a third. Small tasks make playbooks easier to understand, troubleshoot, and reuse.
Playbooks: putting everything together
A playbook combines multiple tasks into one repeatable workflow.
Instead of logging into every server, you run one command.
Ansible connects to every machine in the inventory, determines what needs to change, performs only those changes, and reports the result. That repeatability is what makes automation reliable.
Variables: keeping playbooks flexible
Hardcoding values quickly becomes difficult to maintain. Variables allow the same playbook to work across different environments. For example, instead of embedding a package version directly into every task, you define it once and reference it wherever needed. A production environment and a staging environment can use the same playbook while supplying different variable values. This keeps automation reusable instead of creating multiple copies of nearly identical playbooks.
Your first Ansible workflow
Running Ansible follows the same process every time.
- Write an inventory describing the servers you want to manage.
- Create a playbook that defines the desired state.
- Validate the playbook syntax.
- Execute the playbook against the inventory.
- Review the execution results.
Example: Installing and Starting Nginx
The following playbook ensures that Nginx is installed, the service is running, and it starts automatically after every reboot on all servers in the webservers inventory group.
--- - name: Configure Nginx on web servers hosts: webservers become: true tasks: - name: Ensure Nginx is installed ansible.builtin.apt: name: nginx state: present update_cache: yes - name: Ensure Nginx service is running and enabled ansible.builtin.service: name: nginx state: started enabled: yes … |
Notice how the playbook never describes how to install Nginx. Instead, it describes the desired state:
- Nginx should be installed.
- The service should be running.
- The service should start automatically after boot.
Ansible determines the actions needed to achieve that state.
Running the playbook
Once the inventory and playbook are ready, a typical workflow looks like this:
# Validate syntax ansible-playbook -i inventory.ini playbook.yml --syntax-check # Execute the playbook ansible-playbook -i inventory.ini playbook.yml |
During execution, Ansible reports the status of every task.
| Status | Meaning |
| ok | The system already matched the desired state. |
| changed | Ansible made a change successfully. |
| failed | The task could not be completed. |
| skipped | The task did not meet its execution conditions. |
Learning to read this output is just as important as writing playbooks. A successful execution is not measured by how many tasks ran. It is measured by whether every server reached the desired state without unexpected changes.
The concept beginners misunderstand most
Why idempotency matters
The biggest difference between Ansible and a shell script is not YAML. It is idempotency.An idempotent task produces the same result no matter how many times you run it. If Nginx is already installed, Ansible does not install it again. If a service is already running, Ansible leaves it alone.
- - - name: Ensure Nginx is installed package: name: nginx state: present … |
The keyword here is present. You are not telling Ansible to install Nginx. You are telling it that Nginx should exist. Ansible checks the current state and performs only the changes needed to reach that state.
This makes playbooks safe to run repeatedly. Whether you execute the playbook once or ten times, the end result remains the same. That reliability is one of the reasons teams trust Ansible for routine deployments and server maintenance.
Making automation reusable
As infrastructure grows, copying the same tasks into multiple playbooks quickly becomes difficult to maintain. Ansible provides a few simple building blocks that help you reuse automation without duplicating code.
Variables
Variables let the same playbook work across different environments without hardcoding values such as package names, port numbers (for example, 8080), or application versions. You define these values once and reference them throughout the playbook, making the automation easier to maintain and reuse.
A staging environment and a production environment can use the same playbook while supplying different values.
Roles
A role groups everything needed for one component into a standard directory structure. Instead of maintaining one large playbook, you split automation into smaller, reusable units such as webserver, database, or monitoring.
Most production Ansible projects rely heavily on roles because they are easier to maintain, test, and share across teams.
Handlers
Some tasks should run only when another task changes something.
For example, copying a new Nginx configuration should restart the service, but only if the configuration file actually changed.
Handlers provide exactly that behavior, reducing unnecessary service restarts and making deployments more predictable.
Templates
Configuration files often differ between environments. A template lets you generate those files dynamically by inserting variables into a common template instead of maintaining multiple copies of nearly identical files.
Collections
Collections are packages of Ansible content published by the community or vendors. They include modules, roles, plugins, and documentation for platforms such as AWS, Azure, VMware, Kubernetes, and Cisco devices. Instead of writing everything from scratch, you can build on well-tested automation maintained by the community.
The hidden challenge of Ansible
Learning Ansible syntax is relatively easy. Keeping infrastructure consistent over time is the harder problem.
Configuration drift
Ansible assumes the playbook describes the desired state of your systems. If someone manually edits a server after automation has run, that server gradually drifts away from the rest of the environment.
The simplest way to avoid drift is to treat manual changes as temporary fixes. Once a change is verified, add it to the playbook so every server stays consistent.
Managing secrets
Playbooks often need sensitive information such as database passwords, API keys, or SSH credentials. Storing those values directly in YAML files is a security risk.
Ansible Vault encrypts sensitive variables so they can remain in version control without exposing confidential data. Teams commonly use Vault for secrets that must travel with the playbook, while larger organizations often integrate Ansible with dedicated secret management systems.
Knowing when not to use Ansible
Ansible is excellent for configuring servers and deploying applications, but it is not the right tool for every job.
When Terraform Is the Better Choice
If you are provisioning cloud infrastructure such as VPCs, EC2 instances, load balancers, or Kubernetes clusters, Terraform is usually the better choice, since it tracks infrastructure state and understands dependencies between resources. Ansible has no built-in concept of state; it simply checks the current condition of a system against the playbook each time it runs.
Ansible also isn't the best fit for scenarios that require complex orchestration with strict ordering across many interdependent systems, or environments that are almost entirely Windows-based, where other tools sometimes offer smoother native support.
| Consideration | Ansible | Terraform |
| Infrastructure approach | Mutable — existing servers are patched and updated in place | Immutable — outdated resources are destroyed and replaced, not modified |
| Best suited for | Traditional applications running on long-lived servers or VMs | Cloud-native workloads such as Kubernetes, serverless functions, or auto-scaling groups |
| State management | No built-in state tracking — simply applies the desired configuration each run | Maintains a state file that tracks resources and their dependencies |
| Primary use case | Configuring and maintaining what runs inside a server | Provisioning and managing the infrastructure itself |
Using Both Tools Together
Once infrastructure exists, Ansible becomes an excellent tool for configuring the operating system, installing software, and deploying applications on top of it.
Many production environments use both tools together: Terraform creates the infrastructure, and Ansible configures what runs on it. This combination gives teams a clear separation between infrastructure provisioning and configuration management, with each tool doing the part it's best at, making both easier to maintain over time.
With inventories, modules, tasks, playbooks, and idempotency now familiar, you have the core vocabulary needed to read almost any production Ansible codebase, and to start writing your own.
Frequently asked questions
1. What are Ansible facts?
Ansible facts are system details — like OS type, IP address, memory, and hostname — that Ansible automatically collects from each managed node before running any tasks. They're stored as variables and commonly used for conditional logic (e.g., run a task only on Ubuntu servers) or to dynamically populate configuration files.
2. What is the difference between Ansible ad-hoc commands and playbooks?
An ad-hoc command runs a single task directly from the command line (e.g., ansible webservers -m ping) without writing a YAML file — useful for quick, one-off checks. A playbook, on the other hand, is a reusable, version-controlled YAML file that defines multi-step automation you can run repeatedly.
3. Can Ansible manage Windows servers?
Yes. Instead of SSH, Ansible connects to Windows machines using WinRM (Windows Remote Management), and uses Windows-specific modules such as win_service and win_package instead of the Linux modules like apt or service.
4. How do you do a dry run in Ansible?
You can preview what a playbook would change without actually applying anything by running it with the --check flag, often combined with --diff to see the exact file-level changes. This is different from --syntax-check, which only validates YAML syntax and doesn't simulate execution.
5. Is Ansible free to use?
Yes, Ansible (ansible-core and the community package) is fully open source and free. Red Hat sells a paid enterprise layer called Ansible Automation Platform (formerly Ansible Tower), which adds a web UI, role-based access control, job scheduling, and enterprise support on top of the free core tool.






