Skip to main content

Project Structure

A consistent directory layout makes repositories predictable and reduces onboarding friction. All Ansible projects must follow the structure below.

Repository Layout

.
├── inventories/
│ ├── production/
│ │ ├── hosts.yml
│ │ └── group_vars/
│ │ └── all.yml
│ └── staging/
│ ├── hosts.yml
│ └── group_vars/
│ └── all.yml
├── roles/
│ └── <role-name>/
│ ├── defaults/
│ │ └── main.yml
│ ├── files/
│ ├── handlers/
│ │ └── main.yml
│ ├── meta/
│ │ └── main.yml
│ ├── tasks/
│ │ └── main.yml
│ ├── templates/
│ ├── tests/
│ │ ├── inventory
│ │ └── test.yml
│ └── vars/
│ └── main.yml
├── playbooks/
│ ├── site.yml
│ └── <service>.yml
├── collections/
│ └── requirements.yml
├── ansible.cfg
└── requirements.txt

Key Directories

inventories/

Split inventories by environment. Each environment directory contains a hosts.yml inventory file and a group_vars/ directory for environment-specific variable overrides. Never share a single inventory across environments.

roles/

All reusable automation lives in roles. Each role follows the standard Ansible role directory structure. Roles are self-contained. They must not rely on variables defined outside their own defaults/ or vars/ directories unless those variables are explicitly documented in meta/main.yml.

playbooks/

Playbooks are thin orchestration layers. They import roles and set host targets. Business logic belongs in roles, not playbooks.

collections/

Pin all collection dependencies in collections/requirements.yml. Install with:

ansible-galaxy collection install -r collections/requirements.yml

Ansible Configuration

Every repository must include an ansible.cfg at the root. At minimum:

[defaults]
inventory = inventories/
roles_path = roles/
collections_paths = ~/.ansible/collections
stdout_callback = yaml
interpreter_python = auto_silent

[privilege_escalation]
become = false
become_method = sudo

Setting become = false at the global level forces explicit opt-in per play or per task.