ansible.builtin.file Module: Manage Files, Directories & Symlinks (Complete Guide)
By Luca Berton · Published 2026-04-03 · Category: troubleshooting
Complete guide to ansible.builtin.file module. Create, delete, modify files and directories, set permissions, ownership, symlinks, SELinux context.
The ansible.builtin.file module is one of the most frequently used Ansible modules. It manages files, directories, and symlinks on remote hosts — creating, deleting, and setting permissions.
Basic Syntax
- name: Manage a file
ansible.builtin.file:
path: /path/to/file
state: <file|directory|link|hard|touch|absent>
owner: username
group: groupname
mode: '0755'See also: Ansible Write to File: 5 Methods with Practical Examples (2026)
Create a Directory
- name: Create application directory
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: appuser
group: appuser
mode: '0755'Create nested directories (like mkdir -p)
- name: Create nested directories
ansible.builtin.file:
path: /opt/myapp/config/ssl
state: directory
recurse: trueCreate an Empty File (touch)
- name: Create empty log file
ansible.builtin.file:
path: /var/log/myapp.log
state: touch
owner: appuser
group: appuser
mode: '0644'See also: Ansible file Module: 20 Practical Examples Cookbook (Create, Delete, Permissions)
Create a Symbolic Link
- name: Create symlink
ansible.builtin.file:
src: /opt/myapp/current/config.yml
dest: /etc/myapp/config.yml
state: linkCreate a hard link
- name: Create hard link
ansible.builtin.file:
src: /opt/myapp/app.jar
dest: /opt/myapp/app-latest.jar
state: hardDelete Files and Directories
- name: Remove a file
ansible.builtin.file:
path: /tmp/old-config.yml
state: absent- name: Remove a directory recursively
ansible.builtin.file:
path: /opt/myapp/old-release
state: absentSee also: Ansible Delete File & Remove File: file Module absent State Guide
Set File Permissions
- name: Set permissions with numeric mode
ansible.builtin.file:
path: /opt/myapp/deploy.sh
mode: '0755'
- name: Set permissions with symbolic mode
ansible.builtin.file:
path: /opt/myapp/deploy.sh
mode: 'u+rwx,g+rx,o+rx'
- name: Add execute permission
ansible.builtin.file:
path: /opt/myapp/script.sh
mode: 'a+x'Set Owner and Group
- name: Change file ownership
ansible.builtin.file:
path: /opt/myapp
owner: appuser
group: appgroup
recurse: trueSet SELinux Context
- name: Set SELinux context for web content
ansible.builtin.file:
path: /var/www/html
setype: httpd_sys_content_t
recurse: trueCommon Parameters Reference
| Parameter | Description | Required |
|---|---|---|
path | Path to the file/directory | Yes |
state | file, directory, link, hard, touch, absent | No (default: file) |
owner | Owner of the file | No |
group | Group of the file | No |
mode | Permission mode (octal or symbolic) | No |
recurse | Recursively set attributes (directories only) | No |
src | Source for link/hard state | For links |
force | Force creation of symlinks | No |
follow | Follow symlinks | No |
access_time | Set access time | No |
modification_time | Set modification time | No |
Practical Examples
Ensure application directory structure exists
- name: Create app directory structure
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: appuser
group: appuser
mode: '0755'
loop:
- /opt/myapp
- /opt/myapp/config
- /opt/myapp/logs
- /opt/myapp/dataConditional file management
- name: Create debug log only in development
ansible.builtin.file:
path: /var/log/myapp-debug.log
state: touch
mode: '0666'
when: environment == 'development'FAQ
What's the difference between state: file and state: touch?
state: file verifies a file exists and sets attributes but does NOT create it. state: touch creates the file if it doesn't exist (like the touch command).
How do I create a file with content?
Use ansible.builtin.copy with content parameter instead. The file module only manages file attributes, not content.
Can I use ansible.builtin.file on Windows?
No, use ansible.windows.win_file for Windows hosts.
Create Directory
- name: Create application directory
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: appuser
group: appgroup
mode: '0755'
become: trueCreate Empty File
- name: Create log file
ansible.builtin.file:
path: /var/log/myapp/app.log
state: touch
owner: appuser
mode: '0644'
become: trueCreate Symlink
- name: Link current version
ansible.builtin.file:
src: /opt/myapp-2.5.0
dest: /opt/myapp
state: link
become: trueSet Permissions Recursively
- name: Fix permissions
ansible.builtin.file:
path: /opt/myapp
owner: appuser
group: appgroup
mode: '0755'
recurse: true
become: trueDelete File or Directory
- ansible.builtin.file:
path: /opt/myapp-old
state: absent
become: trueMultiple Directories
- name: Create project structure
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: deploy
mode: '0755'
loop:
- /opt/myapp/config
- /opt/myapp/logs
- /opt/myapp/data
- /opt/myapp/tmp
become: trueVersioned Deployment with Symlinks
- file:
path: "/opt/myapp-{{ version }}"
state: directory
- unarchive:
src: "myapp-{{ version }}.tar.gz"
dest: "/opt/myapp-{{ version }}"
- file:
src: "/opt/myapp-{{ version }}"
dest: /opt/myapp-current
state: link
notify: restart myappState Reference
| State | Description |
|---|---|
file | Set attributes (must exist) |
directory | Create directory (mkdir -p) |
link | Create symbolic link |
hard | Create hard link |
touch | Create empty / update timestamp |
absent | Delete |
FAQ
Why quote mode as '0755'?
YAML interprets unquoted 0755 as decimal 493. Always quote octal permissions.
file vs copy vs template?
file: Create empty files, directories, links, set permissionscopy: Transfer files with contenttemplate: Render Jinja2 templates
Does directory create parents?
Yes — like mkdir -p.
Create Directory
- ansible.builtin.file:
path: /opt/myapp/logs
state: directory
owner: deploy
group: deploy
mode: '0755'
become: trueCreate File
- file:
path: /opt/myapp/.env
state: touch
owner: deploy
mode: '0600'
become: trueCreate Symlink
- file:
src: /opt/myapp/current/config.yml
dest: /etc/myapp/config.yml
state: link
become: trueSet Permissions
- file:
path: /opt/myapp/deploy.sh
mode: '0755'
owner: deploy
group: deploy
become: true
# Recursive permissions
- file:
path: /opt/myapp
state: directory
owner: deploy
group: deploy
recurse: true
become: trueDelete File or Directory
- file:
path: /tmp/old-cache
state: absent
become: trueCreate Multiple Directories
- file:
path: "{{ item }}"
state: directory
owner: deploy
mode: '0755'
loop:
- /opt/myapp
- /opt/myapp/logs
- /opt/myapp/config
- /opt/myapp/data
- /opt/myapp/tmp
become: trueDeployment Directory Structure
- name: Create release directory
file:
path: "/opt/releases/{{ release_version }}"
state: directory
owner: deploy
become: true
- name: Deploy application...
# (copy/git/unarchive tasks)
- name: Update current symlink
file:
src: "/opt/releases/{{ release_version }}"
dest: /opt/myapp/current
state: link
force: true
become: trueSELinux Context
- file:
path: /var/www/myapp
state: directory
setype: httpd_sys_content_t
become: truefile Module States
| State | Action |
|---|---|
file | Modify existing file attributes |
directory | Create directory (+ parents) |
link | Create symbolic link |
hard | Create hard link |
touch | Create empty file / update timestamp |
absent | Delete file or directory |
Key Parameters
| Parameter | Description |
|---|---|
path / dest | Target path |
src | Source for links |
state | Desired state |
owner | File owner |
group | File group |
mode | Permissions (octal string) |
recurse | Apply to contents (directories) |
force | Force link creation |
follow | Follow symlinks |
access_time | Set access time |
modification_time | Set modification time |
FAQ
file vs copy vs template?
file manages file metadata (permissions, ownership, state). copy transfers file content. template processes Jinja2 templates. Use file when you don't need to write content.
Why use '0755' with quotes?
YAML interprets 0755 as octal number 493. Quoting '0755' passes it as a string, which Ansible correctly interprets as permission mode.
How do I create a file with content?
Use copy: content="..." instead. file: state=touch creates an empty file only.
Related Articles
- how Ansible when statements work
- Windows fleet automation with Ansible
- Ansible loop patterns and tips
File Module State Reference
The state parameter controls what the file module does:
| State | Description | Creates | Modifies |
|---|---|---|---|
file | Ensure file exists with attributes | No | Yes (permissions, owner) |
directory | Create directory tree | Yes | Yes |
touch | Create empty file or update timestamp | Yes | Yes |
link | Create symbolic link | Yes | Yes |
hard | Create hard link | Yes | Yes |
absent | Remove file, directory, or link | No | Deletes |
Default State Behavior
When state is not specified, it defaults to file — which means the file must already exist. This is a common pitfall:
# This FAILS if /tmp/myfile doesn't exist
- ansible.builtin.file:
path: /tmp/myfile
mode: '0644'
# Use touch to create, then set permissions
- ansible.builtin.file:
path: /tmp/myfile
state: touch
mode: '0644'Recursive Directory Operations
Create Nested Directories
- name: Create full directory tree
ansible.builtin.file:
path: /opt/app/config/ssl/certs
state: directory
mode: '0755'
owner: app
group: app
# Creates all parent directories automaticallySet Permissions Recursively
- name: Fix permissions on entire directory tree
ansible.builtin.file:
path: /var/www/html
state: directory
recurse: true
owner: www-data
group: www-data
mode: '0755'Warning: recurse: true sets the same mode on both files and directories. For different file/directory permissions, use find with command:
- name: Set directory permissions to 755
ansible.builtin.command: find /var/www -type d -exec chmod 755 {} +
- name: Set file permissions to 644
ansible.builtin.command: find /var/www -type f -exec chmod 644 {} +Working with Symbolic Links
Create Symlink
- name: Link current release
ansible.builtin.file:
src: /opt/app/releases/v2.1.0
dest: /opt/app/current
state: link
force: true
# force: true overwrites existing linkCreate Relative Symlink
- name: Relative symlink within directory
ansible.builtin.file:
src: ../shared/config.yml
dest: /opt/app/current/config.yml
state: linkRemove Broken Symlinks
- name: Find broken symlinks
ansible.builtin.find:
paths: /opt/app
file_type: link
recurse: true
register: all_links
- name: Remove broken symlinks
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ all_links.files }}"
when: not item.islnkSELinux Context Management
- name: Set SELinux context for web content
ansible.builtin.file:
path: /var/www/custom
state: directory
setype: httpd_sys_content_t
seuser: system_u
serole: object_r
- name: Set SELinux context recursively
ansible.builtin.file:
path: /var/www/custom
state: directory
recurse: true
setype: httpd_sys_rw_content_tTemporary Files and Directories
- name: Create temporary directory
ansible.builtin.tempfile:
state: directory
prefix: ansible_deploy_
register: temp_dir
- name: Use temporary directory
ansible.builtin.debug:
msg: "Temp dir: {{ temp_dir.path }}"
- name: Clean up temporary directory
ansible.builtin.file:
path: "{{ temp_dir.path }}"
state: absent
when: temp_dir.path is definedFile Module vs Other Modules
| Task | Module | Why |
|---|---|---|
| Create empty file | file: state=touch | No content needed |
| Create file with content | copy: content=... | Has content |
| Create file from template | template | Jinja2 variables |
| Modify line in file | lineinfile | Single line changes |
| Add text block | blockinfile | Multi-line blocks |
| Download file | get_url | From URL |
| Copy from local | copy: src=... | Local to remote |
| Set permissions only | file: state=file | File already exists |
| Create directory | file: state=directory | Directory creation |
| Create symlink | file: state=link | Symbolic links |
Idempotency Notes
The file module is fully idempotent:
state: directory— Creates only if missing, updates attributes if differentstate: touch— Always updates modification time (reportschangedevery run)state: absent— Only removes if existsstate: link— Creates or updates symlink target
state: touch in production playbooks if you don't want changed on every run. Use state: file to only set attributes on existing files.Category: troubleshooting