Git, CI/CD & GitOpsXXVII · Infrastructure Repository ArchitectureRepoArch
Ansible repository layout — roles, playbooks, inventories, and group_vars
What you'll learn
- Identify the role-based layout of an Ansible repository (roles, playbooks, inventories, group_vars)
- State what belongs in a role versus a top-level playbook
- Apply Galaxy best practices to a role directory structure (tasks, handlers, defaults, vars, meta)
- Recognise why inventories and group_vars live at the repository root, not inside roles
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
The Ansible repository layout is role-based because Ansible’s unit of reuse is the role: a self-contained directory of tasks, handlers, templates, defaults, and vars that applies to any host group. The four top-level directories organise the reusable content, the entry-point execution, and the host-side truth.
The role-based layout
flowchart TB
R["infra-ansible/ (root)"]
R --> RL["roles/"]
R --> PB["playbooks/"]
R --> IN["inventories/"]
R --> GV["group_vars/"]
RL --> RA["roles/common/"]
RL --> RB["roles/webserver/"]
IN --> IA["inventories/prod/"]
IN --> IB["inventories/staging/"]
PB --> PA["playbooks/site.yml"]
The four primary top-level directories:
roles/holds reusable units of automation (webserver, database, common-base). Roles are what playbooks call and what gets shipped to Galaxy.playbooks/holds the entry-point playbooks. A playbook is the unit of execution; a role is the unit of reusable content.inventories/holds the host lists, with a separate directory per environment so secrets and hostnames stay separate.group_vars/holds per-group variables:group_vars/all.ymlapplies to every host;group_vars/web.ymlapplies to every host in thewebgroup.
What goes inside a role
A role’s directory follows the Galaxy standard layout:
roles/webserver/
tasks/main.yml
handlers/main.yml
defaults/main.yml
vars/main.yml
files/
templates/
meta/main.yml
README.md
tasks/main.ymlis the only file the role needs to be callable. Every other directory is optional.defaults/main.ymlhas the lowest variable precedence; it is the role’s public interface.vars/main.ymlhas the highest internal precedence; the caller cannot override it.handlers/main.ymlholds handlers (notified tasks).meta/main.ymldeclares role dependencies and Galaxy metadata.
What goes at the repository root
The repository root holds what applies to every role and every playbook:
ansible.cfg- the project’s Ansible configuration. The repo pin toroles/, the inventory, SSH settings, and the stdout callback.requirements.yml- the list of Galaxy roles and collections.- A top-level
README.mddescribing purpose, environments, and standardansible-playbookinvocations. - A
.gitignoreexcluding*.retry,*.pyc,__pycache__/, and any cleartext vault-encrypted variable files. - A
collections/directory and a vault-password-file reference (the file itself is not in the repository).
Inventories and group_vars
Inventories and group_vars are deliberately at the repository root, not inside any role:
- An inventory file is per environment
(
inventories/prod/hosts,inventories/staging/hosts). - A
group_vars/directory holds variables for those groups;group_vars/web.ymlloads for every host in thewebgroup.
This split is what makes a role portable: the role declares its
inputs through defaults/main.yml; the inventory and
group_vars/ supply the values. The same role applies to dev,
staging, and prod without modification.
Cross-repo consumption
When a role is consumed by another repository, the consumer lists
the role in its own requirements.yml:
roles:
- name: webserver
src: https://github.com/example/infra-ansible.git
scm: git
version: main
path: roles/
The consumer’s ansible-galaxy install -r requirements.yml
clones the role into its own roles/webserver/ and pins it to
the specified ref. Collections follow the same pattern.
Production discipline
- Use the role-based layout from the first playbook. A flat
site.ymlwith hundreds of tasks is technical debt. - One role per coherent unit of configuration. The boundary should be the boundary a new engineer would draw on a whiteboard.
- Keep
defaults/main.ymlandvars/main.ymlapart. Defaults are the public interface; vars are internal constants. - Per-environment inventory, not per-environment playbook. If prod and staging playbooks differ only in variables, the layout is wrong.
- Pin every Galaxy role and every collection in
requirements.yml. Alatestref resolves to whatever the upstream pushed most recently.
Cross-course references
- Ansible for Production Sysadmins - Part IX (Roles) covers the role-call mechanics this layout assumes.
- Linux for Production Sysadmins - Part XXVI (RepoLayout)
covers the filesystem analogue: role, data, and configuration
directories in
/etc. - Git Internals for Production Engineers - Part XXXI covers the cross-repo pattern when a role is consumed by a playbook in another repository.
Quiz
Knowledge check · 4 questions
Q1. An Ansible repository has a `roles/webserver/` role and a `playbooks/site.yml` playbook. Where should the per-environment host list live?
Q2. A `defaults/main.yml` in a role has lower precedence than a `vars/main.yml` in the same role, so values in `vars/main.yml` override values in `defaults/main.yml`.
Q3. Name the four primary top-level directories in a role-based Ansible repository layout, and state the role of each one.
Q4. Recommend the right Ansible layout for a team that has three roles (webserver, database, common-base), two environments (prod, staging), and one application playbook, and identify the file that the application playbook's repository needs in order to consume the webserver role.
A platform team maintains an Ansible repository with a `webserver` role, a `database` role, and a `common` role. An application team maintains a separate repository with an `application.yml` playbook that needs to apply the webserver role to its hosts. The application team does not want to copy the role; they want to consume a pinned version.
Passing score: 75%. Answers are checked in this browser.