In short
Ad-hoc commands are fine for one-offs. Real server setup is twenty steps in order, reused across projects. Playbooks write those steps down, and roles package them so other playbooks can reuse them.
Playbooks define ordered sets of tasks to run against target hosts; Roles organize related playbooks, variables, and files into reusable, shareable units — Ansible's equivalent of Terraform modules.

Think of it like this
The wedding planner's folders
A wedding planner gives each team its own folder: the caterer's has the menu and timings, the decorator's has flowers and colours. The day's schedule just says which teams work when. Roles are those team folders; the playbook is the day's schedule.
Watch it happen
How a playbook uses roles
1/5site.yml is the playbook: which hosts, and which roles to apply to them, in order.
The big ideas
What you need to know
Plays run in order
A playbook is a list of plays. Each targets a host group and runs its tasks top to bottom.
Handlers react to change
Tasks can notify a handler. It only runs if something changed, which avoids needless restarts.
Roles have a fixed layout
tasks, handlers, templates, files, defaults. Run ansible-galaxy init roles/nginx to scaffold one.
Secrets with Vault
ansible-vault encrypts variable files, so passwords can live in the repo without being readable.
See it in code
Walk through the code
1- name: Set up web servers2 hosts: web3 become: true4 vars:5 app_port: 30006 tasks:7 - name: Install nginx8 ansible.builtin.apt: { name: nginx, state: present, update_cache: true }9 - name: Add site config10 ansible.builtin.template: { src: site.conf.j2, dest: /etc/nginx/conf.d/site.conf }11 notify: Reload nginx12 handlers:13 - name: Reload nginx14 ansible.builtin.service: { name: nginx, state: reloaded }1/4One play targeting the web group, with sudo for every task.
Run it with ansible-playbook -i inventory.ini site.yml --check --diff first, then without --check. site.conf.j2 can use {{ app_port }}.
Save yourself the pain
Common mistakes
Oops
Restarting a service as a normal task at the end of every run.
Do this instead
Use a handler with notify, so the service only restarts when its config actually changed.
Oops
Putting every task for every server type into one long playbook.
Do this instead
Split into roles (common, nginx, app) and compose them. Smaller pieces are easier to test and reuse.
Your mission
Turn a playbook into a role
Quick check
Why use a handler to reload nginx instead of a normal task at the end?
Go deeper
- 1Organize a set of related tasks from a playbook into a reusable Ansible Role.
- 2Practice using variables in a playbook to make it reusable across different environments.
- 3Explore Ansible Galaxy for community-maintained roles instead of writing everything from scratch.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.