Ansible is the simplest way to automate servers at scale: you describe the desired state of your machines in YAML playbooks, and Ansible connects over SSH to make it so, with no agent to install on the targets. It configures Linux and Windows hosts, network devices, cloud resources and containers, and it is the usual companion of Terraform (Terraform creates the servers, Ansible configures them). This page is the hub of our Ansible series: it explains the model, installs Ansible on Ubuntu 24.04, runs your first commands and playbook, and lays out the order of the tutorials up to roles and Vault.
Prerequisites: a control machine with Python 3.10+ (Linux, macOS or WSL on Windows), one or more Linux target hosts reachable over SSH with a user that can sudo, and basic YAML (see Ansible YAML syntax).
How Ansible works
- Agentless: the control node opens SSH (or WinRM/SSH for Windows) connections, copies small Python modules to the target, runs them, and removes them.
- Inventory: the list of hosts and groups, static (INI/YAML) or dynamic (AWS, Azure, VMware plugins).
- Modules: thousands of units of work (
apt,copy,service,user,amazon.aws.ec2_instance…), shipped in collections on Ansible Galaxy. - Idempotency: modules check the current state and change only what differs, so running a playbook twice is safe and reports “ok” instead of “changed”.
- Push model: you run Ansible when you want (from a laptop, CI, or AWX/Automation Platform); nothing polls in the background, unlike Puppet or Chef agents.
| Ansible | Puppet / Chef | Terraform | |
|---|---|---|---|
| Agent on target | No | Yes | No (API-based) |
| Language | YAML + Jinja2 | Ruby DSL | HCL |
| Main job | Configure OS and applications | Configure OS and applications | Provision infrastructure |
| Model | Push, procedural order | Pull, declarative catalog | Declarative with state file |
| Learning curve | Hours | Days | Days |
Step 1 – Install Ansible
Install on the control node only. pipx gives you the latest release isolated from the system Python; the Ubuntu PPA is the alternative.
# Ubuntu 24.04 – recommended
sudo apt update && sudo apt install -y pipx
pipx install --include-deps ansible
pipx inject ansible ansible-lint # optional linter
# Alternative: official PPA
# sudo apt-add-repository -y ppa:ansible/ansible && sudo apt install -y ansible
ansible --version
# ansible [core 2.18.x] ...Full details, including macOS and RHEL, in Ansible installation.
Step 2 – Inventory and connectivity
# inventory.ini
[web]
web1 ansible_host=192.168.56.11
web2 ansible_host=192.168.56.12
[db]
db1 ansible_host=192.168.56.21
[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/lab_ed25519
ansible_python_interpreter=/usr/bin/python3ansible -i inventory.ini all -m ping
# web1 | SUCCESS => { "changed": false, "ping": "pong" }
# Ad-hoc commands: one module, no playbook
ansible -i inventory.ini web -m apt -a "name=nginx state=present" --become
ansible -i inventory.ini all -a "uptime"
ansible -i inventory.ini db -m setup -a "filter=ansible_memtotal_mb" # gather factsDeeper dives: inventory files and ad-hoc commands.
Step 3 – Your first playbook
# site.yml
- name: Configure web servers
hosts: web
become: true
vars:
site_name: devops-lab
packages:
- nginx
- ufw
tasks:
- name: Install packages
ansible.builtin.apt:
name: "{{ packages }}"
state: present
update_cache: true
cache_valid_time: 3600
- name: Deploy index page from a template
ansible.builtin.template:
src: templates/index.html.j2
dest: /var/www/html/index.html
mode: "0644"
notify: Reload nginx
- name: Allow HTTP through the firewall
community.general.ufw:
rule: allow
port: "80"
proto: tcp
- name: Ensure nginx is running and enabled
ansible.builtin.service:
name: nginx
state: started
enabled: true
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded<!-- templates/index.html.j2 -->
<h1>{{ site_name }} on {{ inventory_hostname }}</h1>
<p>{{ ansible_distribution }} {{ ansible_distribution_version }}, {{ ansible_processor_vcpus }} vCPU</p>ansible-galaxy collection install community.general
ansible-playbook -i inventory.ini site.yml --check --diff # dry run: what would change
ansible-playbook -i inventory.ini site.yml
# PLAY RECAP
# web1 : ok=5 changed=4 unreachable=0 failed=0
ansible-playbook -i inventory.ini site.yml # second run: changed=0 (idempotent)
curl -s http://192.168.56.11Notice the pattern: tasks use fully-qualified module names, variables and templates keep content out of the playbook, and a handler restarts the service only when something changed.
The learning path
- Ansible introduction – architecture and vocabulary.
- Installation on the control node.
- YAML syntax for playbooks.
- Inventory file – groups, host and group variables.
- Ad-hoc commands.
- File module and the other essential modules (copy, lineinfile, user, service).
- Jinja2 templates – filters, loops, conditionals.
- Roles – reusable structure, Galaxy,
requirements.yml. - Ansible Vault – encrypt passwords and keys in Git.
- Playbook assignment – put it all together.
Where Ansible fits in a DevOps toolchain
- With Terraform: Terraform creates VMs and outputs their IPs; a dynamic inventory (
amazon.aws.aws_ec2) or a generated file feeds Ansible, which installs and configures software. See the Terraform series. - In CI/CD: run
ansible-playbookfrom Jenkins, GitHub Actions or Azure Pipelines with the SSH key stored as a secret; use--limitand--tagsfor targeted runs. - Image building: Packer’s Ansible provisioner bakes golden AMIs; Docker images are better served by a Dockerfile.
- At scale: AWX (open source) or Red Hat Ansible Automation Platform adds a UI, RBAC, scheduling and audit logs.
Good habits
- Run
--check --diffbefore applying to production andansible-lintin CI. - Prefer specific modules (
apt,lineinfile,template) toshell/command; when you must use them, addcreates:orchanged_when:to keep idempotency. - Put secrets in Vault or an external secret manager, never in plain variables.
- Structure real projects as roles from the start; pin collection versions in
requirements.yml. - Use
serial:andmax_fail_percentage:for rolling changes across a fleet.
Troubleshooting the first run
- “Permission denied (publickey)” – wrong
ansible_useror key; test withssh -i ~/.ssh/lab_ed25519 ubuntu@192.168.56.11first. - “Missing sudo password” – the remote user needs passwordless sudo, or pass
--ask-become-pass. - “couldn’t resolve module/action ‘community.general.ufw'” – install the collection with
ansible-galaxy collection install community.general. - Task keeps reporting “changed” – a
shell/commandtask withoutcreates:orchanged_when:; replace it with a proper module.
FAQ
Ansible or Terraform? Both. Terraform for infrastructure lifecycle (create/destroy), Ansible for what runs inside the machines. Ansible can create cloud resources too, but has no state file to track drift.
Windows targets? Yes, through WinRM or OpenSSH with the ansible.windows collection; the control node must still be Linux/macOS/WSL.
Is Ansible still relevant with Kubernetes? Yes: nodes, bastions, databases, network gear and legacy apps still need configuration, and Ansible also drives Kubernetes through the kubernetes.core collection.
Project layout to copy
Even small projects benefit from the standard layout, because roles and inventories then work the same way everywhere and CI can find them without configuration.
ansible-lab/
├── ansible.cfg # inventory path, defaults, callbacks
├── inventories/
│ ├── dev/hosts.yml
│ └── prod/hosts.yml
├── group_vars/
│ ├── all.yml
│ └── web.yml
├── host_vars/
├── roles/
│ ├── common/ # users, packages, hardening
│ └── nginx/ # tasks/ handlers/ templates/ defaults/
├── requirements.yml # collections and Galaxy roles, pinned
└── site.yml # imports the plays per group# ansible.cfg
[defaults]
inventory = inventories/dev/hosts.yml
roles_path = roles
host_key_checking = False
stdout_callback = yaml
interpreter_python = auto_silent
[privilege_escalation]
become = TrueSwitch environments with -i inventories/prod/hosts.yml; everything else stays identical, which is exactly the point of configuration management.
Key takeaways
- Agentless, SSH-based, idempotent, YAML: Ansible is the fastest configuration tool to learn.
- Inventory + modules + playbooks + roles + Vault are the five concepts to master.
- Pair it with Terraform for provisioning and run it from CI for repeatability.
Start the series
Begin with the Ansible introduction, then install Ansible. Official docs: Getting started with Ansible.



