Introduction
Managing file permissions, ownership, and security contexts is fundamental to Linux system automation. The ansible.builtin.file module handles everything from simple chmod/chown operations to SELinux contexts and extended ACLs — all idempotently.
For Windows, use ansible.windows.win_file and ansible.windows.win_acl.
Module Parameters for Permissions
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File or directory path |
mode | string | No | Permissions — octal ('0644') or symbolic ('u=rw,g=r,o=r') |
owner | string | No | User that owns the file |
group | string | No | Group that owns the file |
state | string | No | file, directory, link, hard, touch, absent |
recurse | bool | No | Apply recursively to directory contents |
setype | string | No | SELinux type context |
seuser | string | No | SELinux user context |
selevel | string | No | SELinux level/range |
Important: Always quote octal modes as strings ('0644', not 0644). Without quotes, YAML interprets 0644 as the integer 420.
Basic Examples
Set File Permissions
- name: Set file to 644
ansible.builtin.file:
path: /etc/myapp/config.yml
owner: root
group: root
mode: '0644'
Set Directory Permissions
- name: Create directory with permissions
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: appuser
group: appgroup
mode: '0755'
Change Ownership Only
- name: Change owner
ansible.builtin.file:
path: /var/log/myapp
owner: appuser
group: appgroup
Permission Formats
Octal Notation
# Common permission patterns
mode: '0644' # rw-r--r-- (config files)
mode: '0755' # rwxr-xr-x (directories, scripts)
mode: '0600' # rw------- (private keys, passwords)
mode: '0700' # rwx------ (private directories)
mode: '0775' # rwxrwxr-x (shared directories)
mode: '0444' # r--r--r-- (read-only files)
mode: '1777' # rwxrwxrwt (sticky bit, like /tmp)
mode: '2755' # rwxr-sr-x (setgid directory)
mode: '4755' # rwsr-xr-x (setuid executable)
Symbolic Notation
# Equivalent to 0644
mode: 'u=rw,g=r,o=r'
# Add execute for owner only
mode: 'u+x'
# Remove write for group and others
mode: 'go-w'
# Set exact permissions
mode: 'u=rwx,g=rx,o=rx' # 0755
Practical Examples
Secure SSH Keys
- name: Set SSH directory permissions
ansible.builtin.file:
path: "/home/{{ ansible_user }}/.ssh"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0700'
- name: Set private key permissions
ansible.builtin.file:
path: "/home/{{ ansible_user }}/.ssh/id_rsa"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0600'
- name: Set authorized_keys permissions
ansible.builtin.file:
path: "/home/{{ ansible_user }}/.ssh/authorized_keys"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
Web Server Document Root
- name: Set up web root
ansible.builtin.file:
path: /var/www/mysite
state: directory
owner: www-data
group: www-data
mode: '0755'
recurse: true # Apply to all contents
Recursive Permissions
- name: Fix permissions on application directory
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: appuser
group: appgroup
mode: '0755'
recurse: true
Note: recurse: true sets the same mode on ALL files and directories. To set different permissions for files vs directories, use find + file:
- name: Set directory permissions to 755
ansible.builtin.shell: find /opt/myapp -type d -exec chmod 755 {} \;
- name: Set file permissions to 644
ansible.builtin.shell: find /opt/myapp -type f -exec chmod 644 {} \;
- name: Make scripts executable
ansible.builtin.shell: find /opt/myapp/bin -type f -exec chmod 755 {} \;
Or with the find module:
- name: Find all directories
ansible.builtin.find:
paths: /opt/myapp
file_type: directory
recurse: true
register: app_dirs
- name: Set directory permissions
ansible.builtin.file:
path: "{{ item.path }}"
mode: '0755'
loop: "{{ app_dirs.files }}"
Application Deployment Permissions
- name: Deploy application with correct permissions
hosts: app_servers
become: true
vars:
app_user: appuser
app_group: appgroup
app_dir: /opt/myapp
tasks:
- name: Application directory
ansible.builtin.file:
path: "{{ app_dir }}"
state: directory
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0755'
- name: Config directory (restricted)
ansible.builtin.file:
path: "{{ app_dir }}/config"
state: directory
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0750'
- name: Log directory
ansible.builtin.file:
path: /var/log/myapp
state: directory
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0755'
- name: PID directory
ansible.builtin.file:
path: /var/run/myapp
state: directory
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0755'
- name: Secrets file
ansible.builtin.copy:
src: secrets.yml
dest: "{{ app_dir }}/config/secrets.yml"
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0600'
SELinux Context
- name: Set SELinux context for web content
ansible.builtin.file:
path: /var/www/mysite
state: directory
setype: httpd_sys_content_t
recurse: true
- name: Set SELinux context for writable directory
ansible.builtin.file:
path: /var/www/mysite/uploads
state: directory
setype: httpd_sys_rw_content_t
ACL Permissions
For more granular control, use the ansible.posix.acl module:
- name: Grant read access to monitoring group
ansible.posix.acl:
path: /var/log/myapp
entity: monitoring
etype: group
permissions: rx
state: present
recursive: true
Common Mistakes
Unquoted Octal Mode
# WRONG — YAML interprets 0644 as integer 420
mode: 0644
# CORRECT — always quote octal modes
mode: '0644'
Missing Leading Zero
# WRONG — 644 is not the same as 0644 in all contexts
mode: '644'
# CORRECT — include the leading zero
mode: '0644'
recurse on Files
# WRONG — recurse only works with state: directory
- ansible.builtin.file:
path: /etc/myapp/config.yml
mode: '0644'
recurse: true # Ignored or error — this is a file, not a directory
Related Articles
- Delete Files and Directories
- Create an Empty File: touch
- Copy Files to Remote Hosts
- Ansible lineinfile Module
- Check If a Directory Exists: stat
- Ansible Privilege Escalation
- Ansible Best Practices Guide
Conclusion
The ansible.builtin.file module is your primary tool for managing Linux file permissions, ownership, and security contexts. Always quote octal modes as strings, use recurse: true for directories, and combine with find when files and directories need different permissions. For sensitive files like SSH keys and secrets, enforce 0600; for web content under SELinux, set the appropriate setype. The module is idempotent — running it repeatedly only makes changes when the current state doesn't match the desired state.