Introduction
The Ansible file module (ansible.builtin.file) manages file system objects — create directories, set ownership and permissions, create symlinks, and remove files. It doesn't write file content (use copy, template, or lineinfile for that).
Create a Directory
- name: Create app directory
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: deploy
group: deploy
mode: "0755"
# Create nested directories (like mkdir -p)
- name: Create nested structure
ansible.builtin.file:
path: /opt/myapp/logs/archive
state: directory
owner: deploy
group: deploy
mode: "0755"
Set File Permissions
# Change permissions on existing file
- name: Set script executable
ansible.builtin.file:
path: /opt/myapp/start.sh
mode: "0755"
# Change ownership
- name: Set file ownership
ansible.builtin.file:
path: /opt/myapp/config.yml
owner: deploy
group: deploy
mode: "0640"
# Recursive permissions on directory
- name: Set directory permissions recursively
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: deploy
group: deploy
recurse: true
Create Symlinks
# Create a symlink
- name: Link current release
ansible.builtin.file:
src: /opt/myapp/releases/v2.1.0
dest: /opt/myapp/current
state: link
owner: deploy
group: deploy
# Link config file
- name: Link nginx config
ansible.builtin.file:
src: /opt/myapp/nginx.conf
dest: /etc/nginx/sites-enabled/myapp
state: link
notify: Reload nginx
# Force overwrite existing file/link
- name: Force symlink
ansible.builtin.file:
src: /opt/myapp/releases/v2.2.0
dest: /opt/myapp/current
state: link
force: true
Remove Files and Directories
# Remove a file
- name: Remove old config
ansible.builtin.file:
path: /etc/myapp/old-config.yml
state: absent
# Remove a directory (recursive)
- name: Remove old release
ansible.builtin.file:
path: /opt/myapp/releases/v1.0.0
state: absent
Create Empty File
# Create file if it doesn't exist (touch)
- name: Create log file
ansible.builtin.file:
path: /var/log/myapp/app.log
state: touch
owner: deploy
group: deploy
mode: "0644"
# Touch also updates modification time on existing files
- name: Update timestamp
ansible.builtin.file:
path: /opt/myapp/.last-deploy
state: touch
modification_time: preserve # Keep original mtime
access_time: preserve
Parameters
| Parameter | Description |
|---|---|
path / dest | File/directory path (required) |
state | file, directory, link, hard, touch, absent |
src | Source for symlinks |
owner | File owner |
group | File group |
mode | Permissions (e.g., "0755", "u+rwx,g+rx,o+rx") |
recurse | Apply owner/group/mode recursively (directories only) |
force | Force symlink creation even if dest exists |
follow | Follow symlinks (default: true for file, false for link) |
modification_time | Set mtime (preserve, now, or timestamp) |
access_time | Set atime (preserve, now, or timestamp) |
selevel, setype, seuser | SELinux context |
State Values
| State | Description |
|---|---|
file | Modify existing file (fails if doesn't exist) |
directory | Create directory (and parents) |
link | Create symbolic link |
hard | Create hard link |
touch | Create empty file or update timestamp |
absent | Remove file or directory |
Practical Patterns
Application Directory Structure
- name: Create app directory structure
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: "{{ item.mode | default('0755') }}"
loop:
- { path: "/opt/{{ app_name }}" }
- { path: "/opt/{{ app_name }}/releases" }
- { path: "/opt/{{ app_name }}/shared" }
- { path: "/opt/{{ app_name }}/shared/config" }
- { path: "/opt/{{ app_name }}/shared/log" }
- { path: "/var/log/{{ app_name }}", mode: "0750" }
- { path: "/var/run/{{ app_name }}", mode: "0755" }
Blue-Green Deployment with Symlinks
- name: Create release directory
ansible.builtin.file:
path: "/opt/myapp/releases/{{ release_version }}"
state: directory
owner: deploy
group: deploy
# ... deploy app files ...
- name: Switch to new release
ansible.builtin.file:
src: "/opt/myapp/releases/{{ release_version }}"
dest: /opt/myapp/current
state: link
force: true
notify: Restart application
- name: Clean old releases (keep last 5)
ansible.builtin.shell: |
ls -1dt /opt/myapp/releases/*/ | tail -n +6 | xargs rm -rf
changed_when: false
Secure File Permissions
# SSH directory and keys
- name: SSH directory
ansible.builtin.file:
path: "~{{ user }}/.ssh"
state: directory
owner: "{{ user }}"
group: "{{ user }}"
mode: "0700"
- name: SSH authorized_keys permissions
ansible.builtin.file:
path: "~{{ user }}/.ssh/authorized_keys"
owner: "{{ user }}"
group: "{{ user }}"
mode: "0600"
# SSL certificates
- name: SSL directory
ansible.builtin.file:
path: /etc/ssl/myapp
state: directory
owner: root
group: root
mode: "0700"
- name: Private key permissions
ansible.builtin.file:
path: /etc/ssl/myapp/server.key
owner: root
group: root
mode: "0600"
Check Before Modify
- name: Check if file exists
ansible.builtin.stat:
path: /opt/myapp/config.yml
register: config_file
- name: Set permissions only if exists
ansible.builtin.file:
path: /opt/myapp/config.yml
owner: deploy
mode: "0640"
when: config_file.stat.exists
Mode Format
# Octal notation (quote it!)
mode: "0755" # rwxr-xr-x
mode: "0644" # rw-r--r--
mode: "0600" # rw-------
mode: "0700" # rwx------
# Symbolic notation
mode: "u=rwx,g=rx,o=rx" # 0755
mode: "u+x" # Add execute for owner
mode: "g-w" # Remove write for group
mode: "a+r" # Add read for all
Always quote octal modes — without quotes, 0644 is interpreted as the integer 420.
Troubleshooting
"file not found" with state: file
state: file modifies existing files only — it doesn't create:
# WRONG — fails if file doesn't exist
- file:
path: /tmp/newfile.txt
state: file
mode: "0644"
# RIGHT — create first with touch or copy
- file:
path: /tmp/newfile.txt
state: touch
mode: "0644"
Permissions Not Applied Recursively
recurse only works with state: directory:
# Apply to directory and all contents
- file:
path: /opt/myapp
state: directory
owner: deploy
group: deploy
recurse: true # Sets owner/group on ALL files inside
Symlink Points to Wrong Target
Use force: true to overwrite existing symlinks or files:
- file:
src: /opt/myapp/releases/v2.0
dest: /opt/myapp/current
state: link
force: true
Related Articles
- Ansible copy Module
- Ansible template Module
- Ansible Create Directory Guide
- Ansible lineinfile Module
- Ansible stat Check File Exists
Conclusion
ansible.builtin.file manages file system objects: create directories (state: directory), set permissions (mode, owner, group), create symlinks (state: link), touch files (state: touch), and remove (state: absent). Always quote octal modes. Use recurse: true for directory-wide ownership changes. Combine with copy/template for content and file for permissions. For deployment patterns, symlinks (state: link + force: true) enable instant rollbacks.