Ansible Role Structure Builder

Generate the file tree and main.yml stubs for an Ansible role

Scaffold a complete Ansible role: directory tree plus starter tasks/main.yml, defaults/main.yml, vars/main.yml, handlers/main.yml and meta/main.yml. Matches the layout of ansible-galaxy role init. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is the standard Ansible role directory layout?

A role has tasks, handlers, defaults, vars and meta directories, each with a main.yml, plus optional files and templates folders. This is exactly what ansible-galaxy role init produces, so other Ansible users immediately understand the structure.

The Ansible Role Structure Builder scaffolds a complete role: the standard directory tree plus ready-to-edit main.yml stubs for tasks, defaults, vars, handlers and meta. It mirrors what ansible-galaxy role init generates, so your role is instantly recognisable to anyone who works with Ansible.

How it works

An Ansible role is a fixed set of directories that the engine auto-loads by convention. tasks/main.yml is the entry point. defaults/main.yml and vars/main.yml hold variables at low and high precedence respectively. handlers/main.yml defines tasks that run only when notified — typically a service restart after a config change. meta/main.yml carries Galaxy metadata and role dependencies. Optional files/ and templates/ folders hold static assets and Jinja2 templates.

The tool normalises your role name to the snake_case form Galaxy expects, then emits each file pre-populated with a realistic example task, a notify/handler pair and platform metadata you can edit down to your real values.

Directory layout

my_role/
├── tasks/
│   └── main.yml       ← entry point; include_tasks for larger roles
├── defaults/
│   └── main.yml       ← low-precedence defaults (safe to override)
├── vars/
│   └── main.yml       ← high-precedence internal constants
├── handlers/
│   └── main.yml       ← tasks triggered by notify:
├── meta/
│   └── main.yml       ← Galaxy metadata + role dependencies
├── files/             ← static files copied with copy: module
│   └── .gitkeep
└── templates/         ← Jinja2 templates used with template: module
    └── .gitkeep

Handler wiring example

# tasks/main.yml
- name: Install required package
  ansible.builtin.package:
    name: "{{ my_role_package }}"
    state: present
  notify: Restart service

# handlers/main.yml
- name: Restart service
  ansible.builtin.service:
    name: example
    state: restarted

Ansible deduplicates notifications: if ten tasks all notify: Restart service, the handler runs exactly once, at the end of the play. This is a key reason to use handlers rather than a direct service task for restarts.

defaults vs vars — when to use each

FilePrecedenceUse for
defaults/main.ymlLowestValues the role consumer is expected to override — package names, ports, feature flags
vars/main.ymlHighInternal constants the role should own — internal paths, computed values, things callers should not change

A common mistake is putting everything in vars/, which makes the role hard to configure without editing it directly. Defaults are part of the role’s public interface; vars are its implementation details.

Tips and notes

  • Keep overridable settings in defaults/ and internal constants in vars/.
  • Drop a .gitkeep into any files/ or templates/ folder you want in version control — Git does not track empty directories.
  • Split large task lists using include_tasks: rather than growing tasks/main.yml into hundreds of lines.
  • Run ansible-lint against the generated role before publishing to catch deprecated module names and missing name: keys.