Ansible playbook_dir — Magic Variable for Relative Paths

Introduction

playbook_dir is an Ansible magic variable that contains the absolute path to the directory where the currently running playbook is located. It's essential for building portable automation that references files, templates, and includes using paths relative to the playbook — regardless of where ansible-playbook is executed from.

Quick Reference

- name: Show playbook directory
  ansible.builtin.debug:
    msg: "Playbook is at: {{ playbook_dir }}"
  # Output: "Playbook is at: /home/user/ansible/playbooks"

How It Works

project/
├── playbooks/
│   ├── deploy.yml          ← playbook_dir = /full/path/to/project/playbooks
│   └── maintenance.yml
├── files/
│   └── config.ini
├── templates/
│   └── app.conf.j2
└── inventory/
    └── hosts.yml
# Regardless of where you run from:
cd /tmp
ansible-playbook /home/user/project/playbooks/deploy.yml
# playbook_dir = /home/user/project/playbooks

Common Use Cases

Reference Files Relative to Playbook

- name: Copy file using playbook-relative path
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../files/config.ini"
    dest: /etc/myapp/config.ini

- name: Include vars from parent directory
  ansible.builtin.include_vars:
    file: "{{ playbook_dir }}/../vars/secrets.yml"

Load Templates from Custom Location

- name: Deploy template from shared directory
  ansible.builtin.template:
    src: "{{ playbook_dir }}/../templates/nginx.conf.j2"
    dest: /etc/nginx/nginx.conf

Dynamic Includes

- name: Include environment-specific tasks
  ansible.builtin.include_tasks:
    file: "{{ playbook_dir }}/tasks/{{ env }}.yml"

- name: Include playbook-local role
  ansible.builtin.include_role:
    name: "{{ playbook_dir }}/../roles/common"

Build Paths for Scripts

- name: Run local script
  ansible.builtin.script:
    cmd: "{{ playbook_dir }}/../scripts/setup.sh"

- name: Read local file
  ansible.builtin.set_fact:
    config_content: "{{ lookup('file', playbook_dir + '/../config/defaults.json') }}"

playbook_dir vs Other Path Variables

VariableValueUse When
playbook_dirDirectory of the playbookReferencing files relative to playbook
role_pathDirectory of the current roleInside roles, for role-local files
inventory_dirDirectory of the inventory fileReferencing inventory-relative files
ansible_config_filePath to ansible.cfgRarely needed directly
# Inside a role:
- name: In a role, use role_path
  ansible.builtin.copy:
    src: "{{ role_path }}/files/cert.pem"  # Preferred inside roles
    dest: /etc/ssl/cert.pem

# In a playbook:
- name: In a playbook, use playbook_dir
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/files/cert.pem"  # Preferred in playbooks
    dest: /etc/ssl/cert.pem

With import_playbook and include_playbook

# main.yml (playbook_dir = /project/playbooks)
- ansible.builtin.import_playbook: "{{ playbook_dir }}/sub/deploy.yml"

# Note: In the imported playbook, playbook_dir is STILL the original
# /project/playbooks — NOT /project/playbooks/sub/

Practical Examples

Multi-Environment Deployment

---
- name: Deploy application
  hosts: "{{ target_hosts }}"
  vars:
    env_dir: "{{ playbook_dir }}/../environments/{{ env }}"
  tasks:
    - name: Load environment variables
      ansible.builtin.include_vars:
        dir: "{{ env_dir }}/vars"

    - name: Deploy env-specific config
      ansible.builtin.template:
        src: "{{ env_dir }}/templates/app.conf.j2"
        dest: /etc/myapp/app.conf

Portable Role with Local Files

---
- name: Setup monitoring
  hosts: all
  vars:
    project_root: "{{ playbook_dir }}/.."
  tasks:
    - name: Copy dashboards
      ansible.builtin.copy:
        src: "{{ project_root }}/dashboards/{{ item }}"
        dest: /var/lib/grafana/dashboards/
      loop: "{{ lookup('fileglob', project_root + '/dashboards/*.json', wantlist=True) }}"

Troubleshooting

IssueCauseFix
playbook_dir is emptyBug in very old AnsibleUpgrade to 2.9+
Path doesn't resolveRelative .. gone wrongUse realpath filter: {{ (playbook_dir + '/../files') | realpath }}
Different value in included playbookimport_playbook keeps original dirUse explicit paths
Permission denied on pathUser can't read playbook directoryCheck filesystem permissions

Best Practices

  1. Use playbook_dir in playbooks, role_path in roles — correct scope
  2. Combine with realpath filter — resolves .. to clean absolute paths
  3. Define a project_root var — {{ playbook_dir }}/.. for cleaner references
  4. Don't hardcode absolute paths — use magic variables for portability
  5. Document your directory structure — teammates need to know the layout

Conclusion

playbook_dir gives you the absolute path to your playbook's directory, making file references portable. Use it to load templates, vars files, and scripts relative to the playbook location. Combine with realpath filter for clean paths. Inside roles, prefer role_path instead. Define a project_root variable when you need frequent parent-directory references.