AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

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: true

Create 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)

- name: Create symlink
  ansible.builtin.file:
    src: /opt/myapp/current/config.yml
    dest: /etc/myapp/config.yml
    state: link

Create a hard link

- name: Create hard link
  ansible.builtin.file:
    src: /opt/myapp/app.jar
    dest: /opt/myapp/app-latest.jar
    state: hard

Delete 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: absent

See 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: true

Set SELinux Context

- name: Set SELinux context for web content
  ansible.builtin.file:
    path: /var/www/html
    setype: httpd_sys_content_t
    recurse: true

Common 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/data

Conditional 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: true

Create Empty File

- name: Create log file
  ansible.builtin.file:
    path: /var/log/myapp/app.log
    state: touch
    owner: appuser
    mode: '0644'
  become: true
- name: Link current version
  ansible.builtin.file:
    src: /opt/myapp-2.5.0
    dest: /opt/myapp
    state: link
  become: true

Set Permissions Recursively

- name: Fix permissions
  ansible.builtin.file:
    path: /opt/myapp
    owner: appuser
    group: appgroup
    mode: '0755'
    recurse: true
  become: true

Delete File or Directory

- ansible.builtin.file:
    path: /opt/myapp-old
    state: absent
  become: true

Multiple 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: true
- 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 myapp

State 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 permissions • copy: Transfer files with content • template: 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: true

Create File

- file:
    path: /opt/myapp/.env
    state: touch
    owner: deploy
    mode: '0600'
  become: true
- file:
    src: /opt/myapp/current/config.yml
    dest: /etc/myapp/config.yml
    state: link
  become: true

Set 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: true

Delete File or Directory

- file:
    path: /tmp/old-cache
    state: absent
  become: true

Create 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: true

Deployment 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: true

SELinux Context

- file:
    path: /var/www/myapp
    state: directory
    setype: httpd_sys_content_t
  become: true

file 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 workWindows fleet automation with AnsibleAnsible 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 automatically

Set 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 {} +

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 link

Create Relative Symlink

- name: Relative symlink within directory
  ansible.builtin.file:
    src: ../shared/config.yml
    dest: /opt/app/current/config.yml
    state: link

Remove 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.islnk

SELinux 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_t

Temporary 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 defined

File 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 different • state: touch — Always updates modification time (reports changed every run) • state: absent — Only removes if exists • state: link — Creates or updates symlink target

Tip: Avoid 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

Browse all Ansible tutorials · AnsiblePilot Home