Reading about Ansible is not enough — you need to practice. These exercises progress from basic to intermediate, each building on the last.
Setup
You need two machines: a control node (your laptop) and at least one managed node (VM or container).
Quick lab with Docker:
# Start a target container with SSH
docker run -d --name target \
-p 2222:22 \
rastasheep/ubuntu-sshd:18.04
# Test SSH access
ssh root@localhost -p 2222
# Password: rootCreate inventory.ini:
[lab]
localhost ansible_port=2222 ansible_user=root ansible_password=root ansible_ssh_common_args='-o StrictHostKeyChecking=no'Test connectivity:
ansible -i inventory.ini lab -m pingExercise 1: Ad-hoc Commands
Goal: Run commands on remote hosts without a playbook.
# Check uptime
ansible -i inventory.ini lab -m command -a "uptime"
# Check disk space
ansible -i inventory.ini lab -m command -a "df -h"
# Install a package
ansible -i inventory.ini lab -m apt -a "name=curl state=present" --become
# Create a file
ansible -i inventory.ini lab -m file -a "path=/tmp/hello state=touch"What you learn: Ansible modules work without playbooks too.
Exercise 2: First Playbook
Goal: Write a playbook that installs and configures a web server.
Create exercise2.yml:
---
- name: Setup web server
hosts: lab
become: true
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
update_cache: true
- name: Create custom index page
copy:
content: |
<html>
<body>
<h1>Hello from Ansible!</h1>
<p>Server: {{ inventory_hostname }}</p>
</body>
</html>
dest: /var/www/html/index.html
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: trueRun it:
ansible-playbook -i inventory.ini exercise2.ymlChallenge: Run it twice. Notice how the output shows "changed" vs "ok" — that is idempotency.
Master this topic with hands-on labs
Go beyond reading — build real projects in sandboxed environments with expert video guidance.
Browse Courses →Exercise 3: Variables and Facts
Goal: Use variables and gather system information.
Create exercise3.yml:
---
- name: Variables and facts
hosts: lab
become: true
vars:
app_name: "myapp"
packages:
- htop
- vim
- tree
tasks:
- name: Display OS information
debug:
msg: "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
- name: Display memory info
debug:
msg: "RAM: {{ ansible_memtotal_mb }} MB"
- name: Install packages from list
apt:
name: "{{ packages }}"
state: present
update_cache: true
- name: Create app directory
file:
path: "/opt/{{ app_name }}"
state: directory
mode: "0755"Challenge: Add a variable for the directory mode and make it configurable.
Exercise 4: Conditionals and Loops
Goal: Control task execution with when and iterate with loop.
Create exercise4.yml:
---
- name: Conditionals and loops
hosts: lab
become: true
vars:
users:
- { name: alice, shell: /bin/bash, admin: true }
- { name: bob, shell: /bin/bash, admin: false }
- { name: charlie, shell: /bin/sh, admin: false }
tasks:
- name: Create users
user:
name: "{{ item.name }}"
shell: "{{ item.shell }}"
state: present
loop: "{{ users }}"
- name: Add admin users to sudo group
user:
name: "{{ item.name }}"
groups: sudo
append: true
loop: "{{ users }}"
when: item.admin
- name: Show created admin users
debug:
msg: "{{ item.name }} is an admin"
loop: "{{ users }}"
when: item.adminChallenge: Add a state field to each user and handle present/absent.
Exercise 5: Templates (Jinja2)
Goal: Generate configuration files dynamically.
Create templates/app.conf.j2:
# Application configuration
# Generated by Ansible on {{ ansible_date_time.iso8601 }}
APP_NAME={{ app_name }}
APP_PORT={{ app_port }}
APP_ENV={{ env }}
DB_HOST={{ db_host }}
DB_PORT={{ db_port }}
WORKERS={{ ansible_processor_vcpus * 2 }}Create exercise5.yml:
---
- name: Template exercise
hosts: lab
become: true
vars:
app_name: "webapp"
app_port: 8080
env: "production"
db_host: "db.internal"
db_port: 5432
tasks:
- name: Deploy app configuration
template:
src: templates/app.conf.j2
dest: /etc/app.conf
mode: "0644"
- name: Show generated config
command: cat /etc/app.conf
register: config_output
- name: Display config
debug:
var: config_output.stdout_linesChallenge: Add a list of allowed IPs to the template using a Jinja2 for loop.
Get weekly IT automation tips
Docker, Ansible, Terraform, MLOps — curated insights delivered to your inbox. No spam.
Subscribe Free →Exercise 6: Handlers
Goal: Trigger actions only when changes occur.
Create exercise6.yml:
---
- name: Handler exercise
hosts: lab
become: true
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
notify: Start Nginx
- name: Update Nginx config
copy:
content: |
server {
listen 8080;
location / {
return 200 'Hello from port 8080';
}
}
dest: /etc/nginx/sites-available/custom
notify: Reload Nginx
handlers:
- name: Start Nginx
service:
name: nginx
state: started
- name: Reload Nginx
service:
name: nginx
state: reloadedWhat you learn: Handlers only run when notified by a changed task — efficient and safe.
Exercise 7: Roles
Goal: Organize tasks into a reusable role.
mkdir -p roles/webserver/{tasks,templates,handlers,defaults}roles/webserver/defaults/main.yml:
http_port: 80
server_name: localhostroles/webserver/tasks/main.yml:
---
- name: Install Nginx
apt:
name: nginx
state: present
- name: Deploy site config
template:
src: site.conf.j2
dest: /etc/nginx/sites-available/default
notify: Reload Nginxroles/webserver/handlers/main.yml:
---
- name: Reload Nginx
service:
name: nginx
state: reloadedUse the role:
---
- name: Apply webserver role
hosts: lab
become: true
roles:
- webserverChallenge: Add variables to customize the port and server name.
What's Next?
Our Ansible Automation in 30 Minutes course provides guided exercises in real lab environments — no local VM setup needed. The first lesson is free.
---
Ready to go deeper? Check out our hands-on course: Ansible Quickstart — practical exercises you can follow along on your own machine.
Ready to learn by doing?
Stop reading tutorials — start building. Expert video courses with hands-on labs in real sandboxed environments.
Related Articles
Learn Ansible Free: Beginner Guide
Start learning Ansible for free with hands-on examples. Master playbooks, inventory, modules, and roles to automate infrastructure.
Ansible vs Terraform When to Use
Ansible and Terraform solve different infrastructure problems. Learn when to use each, when to use both together, and how they complement each other.
Ansible Inventory: Static and Dynamic
Master Ansible inventory in INI and YAML formats. Learn host groups, variables, dynamic inventory plugins for AWS and Azure, and patterns for scaling.
Ansible Galaxy Roles Collections
Use Ansible Galaxy to find, install, and manage reusable roles and collections. Learn requirements files, version pinning, and publishing your own content.
Ansible Handlers and Notifications
Master Ansible handlers for triggered actions. Learn to restart services, flush handlers, use handler chains, and implement idempotent notification patterns.
Ansible Infrastructure as Code Guide
Use Ansible as your Infrastructure as Code tool. Learn how playbooks define, version, and automate your entire infrastructure.
Explore topics
Browse more articles on the topics covered here.