Introduction

The ansible.builtin.file module is one of the most versatile modules in Ansible. While it handles everything from creating directories to managing symlinks, one of its most common uses is creating empty files with state: touch — equivalent to the Linux touch command but with Ansible's idempotency and remote execution capabilities.

This guide covers all the ways to use the file module for file creation and management.

Module Reference

Full name: ansible.builtin.file Collection: ansible.builtin (included with Ansible)

Key Parameters

ParameterTypeRequiredDescription
pathstringYesFile or directory path (aliases: dest, name)
statestringNofile, directory, touch, link, hard, absent
modestringNoFile permissions (e.g., '0644', 'u+rw,g-wx')
ownerstringNoFile owner
groupstringNoFile group
recurseboolNoRecursively set attributes (directories only)
modification_timestringNoSet modification time (preserve or timestamp)
access_timestringNoSet access time (preserve or timestamp)

State Values

StateBehavior
touchCreate file if missing; update timestamps if exists
fileVerify file exists and set attributes (fail if missing)
directoryCreate directory (and parents) if missing
absentDelete file or directory
linkCreate symbolic link
hardCreate hard link

Create an Empty File

The simplest use case — create a file if it doesn't exist:

---
- name: Create an empty file
  hosts: all
  tasks:
    - name: Create example.txt
      ansible.builtin.file:
        path: ~/example.txt
        state: touch

With Permissions and Ownership

- name: Create file with specific permissions
  ansible.builtin.file:
    path: /var/log/myapp/app.log
    state: touch
    mode: '0644'
    owner: appuser
    group: appgroup

Create Only If Not Exists (Preserve Timestamps)

By default, state: touch updates the file's timestamps every time. To create the file but not modify it if it already exists:

- name: Create file only if missing (don't update timestamps)
  ansible.builtin.file:
    path: /opt/app/.initialized
    state: touch
    modification_time: preserve
    access_time: preserve

Create a Directory

- name: Create directory with parents
  ansible.builtin.file:
    path: /opt/myapp/logs/archive
    state: directory
    mode: '0755'
    owner: appuser
    group: appgroup

This creates all parent directories if they don't exist — like mkdir -p.

Recursive Permission Setting

- name: Set permissions recursively
  ansible.builtin.file:
    path: /opt/myapp
    state: directory
    mode: '0755'
    owner: appuser
    group: appgroup
    recurse: true

Delete Files and Directories

- name: Remove a file
  ansible.builtin.file:
    path: /tmp/old-config.yml
    state: absent

- name: Remove a directory and all contents
  ansible.builtin.file:
    path: /tmp/build-artifacts
    state: absent
- name: Create symlink
  ansible.builtin.file:
    src: /opt/myapp/releases/v2.1.0
    dest: /opt/myapp/current
    state: link

- name: Create symlink for configuration
  ansible.builtin.file:
    src: /etc/nginx/sites-available/myapp.conf
    dest: /etc/nginx/sites-enabled/myapp.conf
    state: link

Practical Use Cases

Application Deployment Flag Files

- name: Deploy application
  hosts: web_servers
  tasks:
    - name: Download release
      ansible.builtin.get_url:
        url: "https://releases.example.com/app-{{ version }}.tar.gz"
        dest: "/opt/app/releases/app-{{ version }}.tar.gz"

    - name: Extract release
      ansible.builtin.unarchive:
        src: "/opt/app/releases/app-{{ version }}.tar.gz"
        dest: "/opt/app/releases/"
        remote_src: true

    - name: Create deployment marker
      ansible.builtin.file:
        path: "/opt/app/releases/{{ version }}/.deployed"
        state: touch
        mode: '0444'

Log Rotation Preparation

- name: Prepare log directories for new month
  ansible.builtin.file:
    path: "/var/log/myapp/{{ item }}"
    state: directory
    mode: '0755'
    owner: syslog
    group: adm
  loop:
    - "{{ lookup('pipe', 'date +%Y-%m') }}"
    - archive
    - errors

Ensure Configuration Directory Structure

- name: Create application directory structure
  ansible.builtin.file:
    path: "{{ item.path }}"
    state: "{{ item.state | default('directory') }}"
    mode: "{{ item.mode | default('0755') }}"
    owner: appuser
    group: appgroup
  loop:
    - { path: '/opt/myapp' }
    - { path: '/opt/myapp/config' }
    - { path: '/opt/myapp/data', mode: '0700' }
    - { path: '/opt/myapp/logs' }
    - { path: '/opt/myapp/tmp', mode: '0777' }
    - { path: '/opt/myapp/config/app.conf', state: 'touch', mode: '0644' }

Lock Files for Idempotent Tasks

- name: Run migration only once
  block:
    - name: Check for migration lock
      ansible.builtin.stat:
        path: /opt/app/.migration-v2-complete
      register: migration_lock

    - name: Run database migration
      ansible.builtin.command: /opt/app/migrate.sh
      when: not migration_lock.stat.exists

    - name: Create migration lock file
      ansible.builtin.file:
        path: /opt/app/.migration-v2-complete
        state: touch
        mode: '0444'
      when: not migration_lock.stat.exists

Common Mistakes

Forgetting Quotes on Mode

# WRONG — 0644 is interpreted as octal integer (420 decimal)
mode: 0644

# CORRECT — quoted string preserves the octal notation
mode: '0644'

Using touch When file Is Needed

# This creates the file if missing (may not be desired)
state: touch

# This fails if the file doesn't exist (assertion check)
state: file

Path Expansion

# ~ expands on the remote host, not the control node
path: ~/config.yml  # Creates in remote user's home

# Use absolute paths for predictability
path: /home/appuser/config.yml

file Module vs Other Modules

TaskBest Module
Create empty filefile (state: touch)
Create file with contentcopy (with content:)
Create file from templatetemplate
Set permissions onlyfile (state: file)
Create directoriesfile (state: directory)
Copy existing filecopy (with src:)

Conclusion

The ansible.builtin.file module with state: touch is the standard way to create empty files in Ansible. Combined with mode, owner, and group parameters, it handles everything from simple file creation to complex directory structures. Use modification_time: preserve when you need to create files without updating timestamps on subsequent runs, and use state: absent for clean removal of files and directories.