Introduction
When building Ansible playbooks that reference local files — templates, scripts, variable files, or custom modules — you need a reliable way to construct file paths that work regardless of where ansible-playbook is invoked from. The playbook_dir magic variable provides exactly this: the absolute filesystem path to the directory containing the playbook being executed.
This article covers playbook_dir usage, all related magic variables, practical patterns for portable path construction, and common pitfalls.
What Is playbook_dir?
playbook_dir is a magic variable (also called a special variable) that Ansible sets automatically. It contains the absolute path to the directory of the playbook that was passed to the ansible-playbook command.
# If you run:
# ansible-playbook /home/deploy/playbooks/site.yml
# Then:
# playbook_dir = "/home/deploy/playbooks"
Key characteristics:
- Always available — no need to enable
gather_facts - Set by Ansible itself — cannot be overridden in inventory or vars
- Absolute path — always a full filesystem path
- Directory only — does not include the playbook filename
Basic Usage
---
- name: Display playbook directory
hosts: all
gather_facts: false
tasks:
- name: Print playbook_dir
ansible.builtin.debug:
var: playbook_dir
Execution
$ ansible-playbook -i inventory playbooks/site.yml
PLAY [Display playbook directory] *********
TASK [Print playbook_dir] *****************
ok: [demo.example.com] => {
"playbook_dir": "/home/deploy/playbooks"
}
Practical Use Cases
1. Reference Local Files Portably
- name: Copy script from playbook directory
ansible.builtin.copy:
src: "{{ playbook_dir }}/files/deploy.sh"
dest: /opt/scripts/deploy.sh
mode: "0755"
Without playbook_dir, the path would break if you cd to a different directory before running the playbook.
2. Dynamic Variable File Includes
- name: Load environment-specific variables
ansible.builtin.include_vars:
file: "{{ playbook_dir }}/vars/{{ env }}.yml"
3. Reference Templates Outside roles/
- name: Deploy config from shared templates
ansible.builtin.template:
src: "{{ playbook_dir }}/../shared/templates/app.conf.j2"
dest: /etc/myapp/app.conf
4. Store Output Files Relative to Playbook
- name: Save inventory report
ansible.builtin.copy:
content: "{{ ansible_facts | to_nice_json }}"
dest: "{{ playbook_dir }}/reports/{{ inventory_hostname }}.json"
delegate_to: localhost
5. Include Tasks from Sibling Directories
# Directory structure:
# project/
# ├── playbooks/
# │ └── site.yml
# └── tasks/
# └── common.yml
- name: Include shared tasks
ansible.builtin.include_tasks: "{{ playbook_dir }}/../tasks/common.yml"
6. Custom Module or Plugin Path
# ansible.cfg can reference playbook_dir dynamically:
# But in practice, set it in ansible.cfg or via env var
- name: Run custom module
my_custom_module:
config: "{{ playbook_dir }}/configs/module_config.json"
All Ansible Magic Variables for Paths
| Variable | Description | Example |
|---|---|---|
playbook_dir | Directory of the current playbook | /home/deploy/playbooks |
inventory_dir | Directory of the inventory file | /home/deploy/inventory |
inventory_file | Full path to the inventory file | /home/deploy/inventory/hosts |
role_path | Path to the current role directory | /home/deploy/roles/webserver |
ansible_config_file | Path to the active ansible.cfg | /home/deploy/ansible.cfg |
Comparison: playbook_dir vs inventory_dir
- name: Show both paths
ansible.builtin.debug:
msg: |
Playbook dir: {{ playbook_dir }}
Inventory dir: {{ inventory_dir }}
Inventory file: {{ inventory_file }}
# Running:
# ansible-playbook -i /etc/ansible/hosts playbooks/site.yml
# playbook_dir = "/home/deploy/playbooks"
# inventory_dir = "/etc/ansible"
# inventory_file = "/etc/ansible/hosts"
role_path Inside Roles
Within a role, role_path points to the role's root directory:
# roles/webserver/tasks/main.yml
- name: Show role path
ansible.builtin.debug:
var: role_path
# Output: "/home/deploy/roles/webserver"
Other Useful Magic Variables
| Variable | Description |
|---|---|
inventory_hostname | Current host from inventory |
inventory_hostname_short | Short hostname (before first dot) |
ansible_play_name | Name of the current play |
ansible_play_hosts | List of active hosts in current play |
ansible_version | Ansible version info dict |
groups | Dict of all inventory groups |
hostvars | Dict of all host variables |
group_names | Groups the current host belongs to |
ansible_check_mode | True if running in check mode |
ansible_diff_mode | True if running with --diff |
- name: Show useful magic variables
ansible.builtin.debug:
msg: |
Host: {{ inventory_hostname }}
Play: {{ ansible_play_name }}
Check mode: {{ ansible_check_mode }}
Ansible version: {{ ansible_version.full }}
Playbook dir: {{ playbook_dir }}
Building Portable Playbooks
Pattern: Project-Relative Paths
# Project structure:
# myproject/
# ├── ansible.cfg
# ├── inventory/
# ├── playbooks/
# │ ├── site.yml
# │ └── deploy.yml
# ├── files/
# ├── templates/
# └── vars/
# In playbooks/site.yml:
vars:
project_root: "{{ playbook_dir }}/.."
files_dir: "{{ playbook_dir }}/../files"
templates_dir: "{{ playbook_dir }}/../templates"
vars_dir: "{{ playbook_dir }}/../vars"
Pattern: Environment-Aware Config Loading
- name: Load environment config
ansible.builtin.include_vars:
file: "{{ playbook_dir }}/environments/{{ target_env }}/config.yml"
- name: Load secrets
ansible.builtin.include_vars:
file: "{{ playbook_dir }}/environments/{{ target_env }}/vault.yml"
Pattern: Dynamic Inventory Scripts
- name: Run inventory script from playbook dir
ansible.builtin.command: "{{ playbook_dir }}/scripts/discover_hosts.py"
register: discovered_hosts
delegate_to: localhost
changed_when: false
Common Pitfalls
1. playbook_dir Changes with Imported Playbooks
When using import_playbook, playbook_dir reflects the imported playbook's directory, not the importing one:
# /home/deploy/main.yml imports /home/deploy/sub/tasks.yml
# In tasks.yml: playbook_dir = "/home/deploy/sub" (not /home/deploy)
2. Relative Paths in Roles
Inside roles, Ansible automatically resolves paths relative to the role directory. You usually don't need playbook_dir for role files:
# roles/webserver/tasks/main.yml
- name: Deploy template
ansible.builtin.template:
src: nginx.conf.j2 # Automatically looks in roles/webserver/templates/
dest: /etc/nginx/nginx.conf
3. playbook_dir Is Not the Working Directory
playbook_dir is where the playbook file lives, not where you ran ansible-playbook from:
# Working directory: /home/user
# Playbook location: /opt/ansible/playbooks/site.yml
$ ansible-playbook /opt/ansible/playbooks/site.yml
# playbook_dir = "/opt/ansible/playbooks" (NOT /home/user)
4. Cannot Override playbook_dir
# ❌ This does NOT work — magic variables cannot be set by users
vars:
playbook_dir: "/custom/path"
# The variable will still contain the real playbook directory
Debugging Path Issues
- name: Debug all path variables
ansible.builtin.debug:
msg: |
playbook_dir: {{ playbook_dir }}
inventory_dir: {{ inventory_dir | default('not set') }}
inventory_file: {{ inventory_file | default('not set') }}
role_path: {{ role_path | default('not in a role') }}
ansible_config_file: {{ ansible_config_file | default('not set') }}
Related Articles
- Ansible Variables Guide
- Ansible debug Module
- Ansible Playbook Guide
- Ansible include_tasks vs import_tasks
Conclusion
The playbook_dir magic variable is essential for building portable, path-independent playbooks. Use it to construct reliable file references for templates, scripts, variable files, and reports. Combined with inventory_dir, role_path, and other magic variables, you can create playbook projects that work correctly regardless of where they are cloned or executed from. Always use absolute path construction with playbook_dir instead of relying on the shell working directory.