Introduction to Ansible — Automate Infrastructure Without an Agent
Before configuration management tools, sysadmins maintained hundreds of servers by hand — logging in, running commands, hoping nothing went wrong. This was error-prone, slow, and impossible to scale. Ansible was built to solve exactly this problem.
What makes Ansible different from Chef and Puppet is that it's agentless. No daemon running on your servers, no certificates to manage, no port to open beyond SSH. Ansible runs from your machine, connects over SSH, executes tasks, and disconnects.
By the end of this article you'll understand Ansible's core concepts — inventory, playbooks, tasks, and modules — and you'll have a working playbook that installs and configures Nginx on a remote server.
Inventory, Playbooks, and Modules — The Three Core Concepts
Ansible has three building blocks. The inventory is a list of servers you want to manage — IP addresses or hostnames, organised into groups. A playbook is a YAML file describing what to do on which servers. A module is a pre-built unit of work — install a package, copy a file, start a service. Ansible ships with thousands of modules covering everything from AWS to Windows registry.
# inventory.ini — tell Ansible which servers exist [webservers] 192.168.1.10 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa 192.168.1.11 ansible_user=ubuntu [databases] 192.168.1.20 ansible_user=ubuntu [production:children] # Group of groups webservers databases
ansible webservers -i inventory.ini -m ping
192.168.1.10 | SUCCESS => {"ping": "pong"}
192.168.1.11 | SUCCESS => {"ping": "pong"}
Your First Playbook
A playbook is a list of plays. Each play targets a group of servers and runs a list of tasks. Each task calls one Ansible module. The order is: playbook → plays → tasks → modules. Ansible executes tasks top-to-bottom, on all targeted servers in parallel.
--- - name: Configure web servers # Play name — describes intent hosts: webservers # Target the webservers group from inventory become: true # Use sudo for privileged operations tasks: - name: Update apt package cache apt: update_cache: yes cache_valid_time: 3600 # Only update if cache is older than 1 hour - name: Install Nginx apt: name: nginx state: present # 'present' = install, 'absent' = remove, 'latest' = upgrade - name: Copy custom Nginx config copy: src: files/nginx.conf # File on your local machine dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' notify: Restart Nginx # Trigger handler only if this task changes something - name: Ensure Nginx is running and starts on boot service: name: nginx state: started enabled: yes handlers: # Only run when notified, and only once even if notified multiple times - name: Restart Nginx service: name: nginx state: restarted
PLAY [Configure web servers] ******************
TASK [Update apt package cache] *** ok: [192.168.1.10]
TASK [Install Nginx] *** changed: [192.168.1.10]
TASK [Copy custom Nginx config] *** changed: [192.168.1.10]
TASK [Ensure Nginx is running] *** ok: [192.168.1.10]
RUNNING HANDLER [Restart Nginx] *** changed: [192.168.1.10]
PLAY RECAP: ok=4 changed=3 unreachable=0 failed=0
| Tool | Agent Required | Language | Learning Curve | Best For |
|---|---|---|---|---|
| Ansible | No (agentless) | YAML | Low | Simple automation, ad-hoc tasks |
| Chef | Yes (chef-client) | Ruby DSL | High | Complex policy-based config |
| Puppet | Yes (puppet agent) | Puppet DSL | High | Large enterprise, compliance |
| Terraform | No | HCL | Medium | Infrastructure provisioning (not config) |
🎯 Key Takeaways
- Ansible is agentless — it connects over SSH, no software needed on managed servers
- Playbooks are YAML files describing the desired state of your infrastructure
- Always use dedicated modules (apt, service, copy) over shell — they are idempotent
- Handlers run once at the end of a play, only if notified — perfect for service restarts
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Using shell/command modules when a dedicated module exists — ansible.builtin.shell: apt install nginx works but is not idempotent. Use the apt module instead — it checks current state before acting.
- ✕Mistake 2: Not using become: true for tasks that need sudo — tasks silently fail with permission denied. Add become: true at play level for servers where all tasks need elevated privileges.
- ✕Mistake 3: Hardcoding passwords in playbooks — use Ansible Vault to encrypt secrets. Run ansible-vault encrypt_string 'mypassword' to get an encrypted value you can safely commit to Git.
Interview Questions on This Topic
- QWhat does agentless mean in Ansible, and what protocol does it use to connect to servers?
- QWhat is idempotency and why is it important for configuration management?
- QWhat is the difference between a task and a handler in an Ansible playbook?
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.