API & Automation·8 min read·

Ansible Roles and Tags for Reusable Automation

Organize growing playbooks into reusable roles with defaults, handlers, and tags so you can run only the steps you need at any moment.

NB

Netbay Infrastructure Team

Netbay Engineering

On this page

Single playbooks hit a wall around the moment they cross one hundred tasks. Files get long, someone forgets which variable controls the port, and running the whole thing for one small change becomes a ceremony. Roles are the answer: they bundle tasks, handlers, templates, and variables into a named, reusable unit, and tags let you slice a playbook so only the relevant parts run.

Anatomy of a Role

A role is just a directory with a conventional layout. The skeleton below is the one Ansible expects by default:

bash
roles/nginx/
├── tasks/main.yml        # the tasks the role runs
├── handlers/main.yml     # handlers triggered by notify
├── templates/            # Jinja2 config templates
│   └── nginx.conf.j2
├── vars/main.yml         # high-precedence variables
├── defaults/main.yml     # low-precedence safe defaults
└── meta/main.yml         # role metadata and dependencies

Create the tree by hand or scaffold it with the ansible-galaxy init nginx command inside roles/. The key ordering fact to remember: defaults/main.yml has the lowest precedence of any variable source, so consumers can always override your defaults, while vars/main.yml is deliberately hard to override — use it only for genuine internals.

Wiring Roles Into a Playbook

Roles attach to a play exactly where inline tasks would live, and can take per-role variable overrides at the point of use:

yaml
- name: Apply the web tier
  hosts: webservers
  roles:
    - role: common
      vars:
        install_updates: false
    - role: nginx
      vars:
        listen_port: 8080

The same nginx role can now serve one site on port 80 in staging and another on 8080 in production purely by overriding listen_port where the role is used. Handlers inside the role fire when a task notifies them, which means a config change triggers a reload only when the template actually changed — the efficiency win that keeps restarts honest.

Tags: Run Only What You Need

Tags attach to roles, tasks, or whole blocks, then act as filters at the command line. They turn one big playbook into a toolbox you can open at a specific drawer:

yaml
- name: Apply the web tier
  hosts: webservers
  roles:
    - role: common
      tags: [base, security]
    - role: nginx
      tags: [web, nginx]
bash
ansible-playbook site.yml --tags nginx
ansible-playbook site.yml --tags "base,web" --skip-tags security

The first command runs only nginx-related work; the second runs base and web but excludes anything tagged security, which is exactly how you would skip a risky security step during an incident. Tags also work with --list-tags to audit what a playbook can do before you run it.

Role anatomy and tagged runs site.yml roles list role: nginx ok / changed tasks/main.yml handlers/main.yml templates/ defaults + vars --tags

Takeaway

Refactoring a long playbook into roles is mechanical work with outsized reward: each role becomes independently testable, overridable, and reusable across projects. Combine that with disciplined tagging and you get a config system you can run surgically during incidents instead of all-or-nothing. A solid way to develop this muscle is to rebuild your own Netbay boxes with roles — reprovision test instances on netbayhosts.in and iterate freely.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance