How to Create a Directory in Ansible
Creating directories is one of the most common Ansible tasks. The ansible.builtin.file module with state: directory creates single or nested directories, sets ownership and permissions, and is idempotent — running it again changes nothing if the directory already exists.
The shortest reliable pattern is ansible.builtin.file with path: /your/directory and state: directory; add owner, group, and mode when the directory needs specific permissions.
Basic Syntax
- name: Create a directory
ansible.builtin.file:
path: /opt/myapp
state: directory
Parameters
| Parameter | Default | Description |
|---|---|---|
path | (required) | Directory path to create |
state | file | Set to directory to create a directory |
owner | — | Owner user name or UID |
group | — | Group name or GID |
mode | — | Permissions (e.g., "0755", "u=rwx,g=rx,o=rx") |
recurse | false | Apply owner/group/mode recursively to contents |
selevel | — | SELinux level |
setype | — | SELinux type |
Create a Single Directory
---
- name: Create application directory
hosts: all
become: true
tasks:
- name: Create /opt/myapp
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: appuser
group: appgroup
mode: "0755"
Create Nested Directories
state: directory creates parent directories automatically (like mkdir -p):
- name: Create nested path (parents created automatically)
ansible.builtin.file:
path: /opt/myapp/releases/v2.1.0/config
state: directory
owner: deploy
mode: "0755"
# Creates: /opt/myapp, /opt/myapp/releases, /opt/myapp/releases/v2.1.0, and /opt/myapp/releases/v2.1.0/config
Create Multiple Directories
---
- name: Create application directory structure
hosts: all
become: true
vars:
app_root: /opt/myapp
directories:
- "{{ app_root }}/config"
- "{{ app_root }}/data"
- "{{ app_root }}/logs"
- "{{ app_root }}/tmp"
- "{{ app_root }}/backups"
- "{{ app_root }}/scripts"
tasks:
- name: Create all directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: appuser
group: appgroup
mode: "0755"
loop: "{{ directories }}"
Set Permissions
---
- name: Create directories with specific permissions
hosts: all
become: true
tasks:
# Standard application directory
- name: Create app directory (owner rwx, group rx, other rx)
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: deploy
group: deploy
mode: "0755"
# Restricted config directory
- name: Create config directory (owner only)
ansible.builtin.file:
path: /opt/myapp/config
state: directory
owner: root
group: root
mode: "0700"
# Shared writable directory
- name: Create shared data directory with setgid
ansible.builtin.file:
path: /opt/myapp/shared
state: directory
owner: root
group: appgroup
mode: "2775" # setgid — new files inherit group
# World-readable log directory
- name: Create log directory
ansible.builtin.file:
path: /var/log/myapp
state: directory
owner: appuser
group: adm
mode: "0755"
# Temp directory with sticky bit
- name: Create temp directory with sticky bit
ansible.builtin.file:
path: /opt/myapp/tmp
state: directory
mode: "1777" # sticky bit — only owner can delete files
Check If Directory Exists
---
- name: Conditional directory operations
hosts: all
tasks:
- name: Check if directory exists
ansible.builtin.stat:
path: /opt/myapp
register: app_dir
- name: Show result
ansible.builtin.debug:
msg: "Directory exists: {{ app_dir.stat.exists and app_dir.stat.isdir }}"
- name: Create only if missing
ansible.builtin.file:
path: /opt/myapp
state: directory
when: not app_dir.stat.exists
Note: The stat check is usually unnecessary — file with state: directory is already idempotent. Use stat only when you need conditional logic based on existence.
Apply Permissions Recursively
- name: Fix permissions on entire directory tree
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: deploy
group: deploy
recurse: true # Applies to all files and subdirectories
⚠️ recurse: true sets the same owner/group/mode on all contents. If files and directories need different permissions, use find + file instead:
- name: Find all directories
ansible.builtin.find:
paths: /opt/myapp
file_type: directory
recurse: true
register: dirs
- name: Set directory permissions (755)
ansible.builtin.file:
path: "{{ item.path }}"
mode: "0755"
loop: "{{ dirs.files }}"
- name: Find all files
ansible.builtin.find:
paths: /opt/myapp
file_type: file
recurse: true
register: files
- name: Set file permissions (644)
ansible.builtin.file:
path: "{{ item.path }}"
mode: "0644"
loop: "{{ files.files }}"
Practical Patterns
Web Application Directory Structure
---
- name: Set up web application
hosts: webservers
become: true
vars:
app: mywebapp
user: www-data
tasks:
- name: Create application directories
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ item.owner | default(user) }}"
group: "{{ user }}"
mode: "{{ item.mode | default('0755') }}"
loop:
- { path: "/var/www/{{ app }}" }
- { path: "/var/www/{{ app }}/public" }
- { path: "/var/www/{{ app }}/storage", mode: "0775" }
- { path: "/var/www/{{ app }}/storage/logs", mode: "0775" }
- { path: "/var/www/{{ app }}/storage/cache", mode: "0775" }
- { path: "/var/www/{{ app }}/config", owner: "root", mode: "0750" }
- { path: "/var/log/{{ app }}", owner: "root" }
Temporary Directory with Cleanup
- name: Create temp build directory
ansible.builtin.file:
path: /tmp/build-{{ ansible_date_time.epoch }}
state: directory
mode: "0700"
register: build_dir
- name: Use the directory
ansible.builtin.debug:
msg: "Building in {{ build_dir.path }}"
- name: Clean up when done
ansible.builtin.file:
path: "{{ build_dir.path }}"
state: absent
Troubleshooting
"Permission denied"
You need become: true to create directories outside the Ansible user's home:
- name: Create system directory
ansible.builtin.file:
path: /opt/myapp
state: directory
become: true
Directory Created with Wrong Permissions
Quote the mode as a string to prevent YAML from interpreting it as an octal number:
# Wrong — YAML parses 0755 as integer 493
mode: 0755
# Right — string, interpreted correctly
mode: "0755"
"chown failed: failed to look up user"
The owner user doesn't exist on the target. Create the user first:
- name: Create app user
ansible.builtin.user:
name: appuser
system: true
- name: Create directory owned by appuser
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: appuser
Related Articles
- Ansible file Module — Files, Symlinks & Permissions
- Check if File Exists (stat module)
- Ansible copy Module
- Ansible template Module
- Ansible win_file Module (Windows)
- File Permissions Guide
Conclusion
Create directories with ansible.builtin.file and state: directory. Parent directories are created automatically. Always quote mode as a string ("0755" not 0755). Use recurse: true to apply ownership changes to all contents, but prefer find + file when files and directories need different permissions. The module is idempotent — it only makes changes when the directory is missing or permissions differ.