Ansible Check if File Exists: stat Module with when Conditional (Guide)
By Luca Berton · Published 2024-01-01 · Category: installation
How to check if a file exists in Ansible with the stat module and when conditional. Verify file existence, check directories, test before actions.

How to check if a file exists in Ansible?
See also: Ansible Check If Directory Exists: stat Module Guide
Ansible check file exists
Today we're talking about the Ansible modulestat.
The full name is ansible.builtin.stat, which means that is part of the collection of modules "builtin" with ansible and shipped with it.
It's a module pretty stable and out for years.
It works in a different variety of operating systems.
It retrieves a file entry or a file system status.
For Windows target use the ansible.windows.win_stat module instead.
Mandatory Parameters
- path string
Main Return Values
- stat complex - exists
## Playbook Let's jump in a real-life playbook to check if a file exists with Ansible.
code
- file_exist.yml
---
- name: check if a file exist
hosts: all
become: false
vars:
myfile: /home/devops/test.txt
tasks:
- name: check if a file exists
ansible.builtin.stat:
path: "{{ myfile }}"
register: file_data
- name: report file exists
ansible.builtin.debug:
msg: "The file {{ myfile }} exist"
when: file_data.stat.exists
- name: report file not exists
ansible.builtin.debug:
msg: "The file {{ myfile }} doesn't exist"
when: not file_data.stat.existsSee also: Ansible win_stat: Check if File or Directory Exists on Windows (Examples)
Conclusion
Now you know how to check if a file exists with Ansible.Advanced File Checks
Check file and take action
- name: Check if config exists
ansible.builtin.stat:
path: /etc/myapp/config.yml
register: config_file
- name: Copy default config if missing
ansible.builtin.copy:
src: files/default-config.yml
dest: /etc/myapp/config.yml
mode: '0644'
when: not config_file.stat.exists
- name: Read existing config
ansible.builtin.slurp:
src: /etc/myapp/config.yml
register: config_content
when: config_file.stat.existsVerify file checksum (integrity check)
- name: Get file checksum
ansible.builtin.stat:
path: /opt/myapp/binary
checksum_algorithm: sha256
register: binary_info
- name: Fail if binary is corrupted
ansible.builtin.fail:
msg: "Binary checksum mismatch! Expected {{ expected_checksum }}"
when:
- binary_info.stat.exists
- binary_info.stat.checksum != expected_checksumCheck file age
- name: Get file info
ansible.builtin.stat:
path: /var/log/myapp/app.log
register: log_file
- name: Rotate if file is larger than 100MB
ansible.builtin.command: logrotate -f /etc/logrotate.d/myapp
when:
- log_file.stat.exists
- log_file.stat.size > 104857600
become: trueCheck multiple files exist
- name: Check required files
ansible.builtin.stat:
path: "{{ item }}"
register: required_files
loop:
- /etc/ssl/certs/myapp.crt
- /etc/ssl/private/myapp.key
- /etc/myapp/config.yml
- name: Fail if any required file is missing
ansible.builtin.fail:
msg: "Missing required file: {{ item.item }}"
loop: "{{ required_files.results }}"
when: not item.stat.existsCheck if file is a symlink
- name: Check path type
ansible.builtin.stat:
path: /usr/bin/python
register: python_path
- name: Show Python path details
ansible.builtin.debug:
msg: >
Path: /usr/bin/python
Exists: {{ python_path.stat.exists }}
Is symlink: {{ python_path.stat.islnk | default(false) }}
Links to: {{ python_path.stat.lnk_source | default('N/A') }}See also: Ansible Set File Permissions 755: chmod with file Module Guide
stat Module vs Other Approaches
| Approach | Use Case |
|---|---|
stat + when | Check before acting (most flexible) |
copy with force: false | Copy file only if dest doesn't exist |
file with state: file | Fails if file doesn't exist |
lineinfile with create: true | Create file if missing, add line |
template | Always overwrites with template output |
FAQ
How do I check if a file exists on Windows?
- name: Check file on Windows
ansible.windows.win_stat:
path: C:\Program Files\MyApp\config.ini
register: win_file
- name: File status
ansible.builtin.debug:
msg: "File exists: {{ win_file.stat.exists }}"Why does stat fail with "Permission denied"?
The Ansible connection user needs read access to the parent directory. Use become: true to check files in restricted directories.
How do I get the file's content along with stat?
stat doesn't read content. Use slurp (for small files) or fetch (to download):
- name: Read file content
ansible.builtin.slurp:
src: /etc/myapp/config.yml
register: file_content
when: config_file.stat.exists
- name: Decode content
ansible.builtin.debug:
msg: "{{ file_content.content | b64decode }}"Check File Exists
- ansible.builtin.stat:
path: /etc/myapp/config.yml
register: config_file
- name: Create config only if missing
ansible.builtin.template:
src: config.yml.j2
dest: /etc/myapp/config.yml
when: not config_file.stat.exists
become: trueCheck Multiple Files
- stat: { path: "{{ item }}" }
loop:
- /etc/nginx/nginx.conf
- /etc/myapp/config.yml
- /opt/myapp/app.jar
register: file_checks
- debug:
msg: "{{ item.item }} exists: {{ item.stat.exists }}"
loop: "{{ file_checks.results }}"File Type Checks
- stat: { path: /opt/myapp }
register: path
- debug:
msg:
- "Is file: {{ path.stat.isreg | default(false) }}"
- "Is directory: {{ path.stat.isdir | default(false) }}"
- "Is symlink: {{ path.stat.islnk | default(false) }}"
- "Is socket: {{ path.stat.issock | default(false) }}"File Properties
- stat: { path: /opt/myapp/deploy.sh }
register: script
- debug:
msg:
- "Size: {{ script.stat.size }} bytes"
- "Mode: {{ script.stat.mode }}"
- "Owner: {{ script.stat.pw_name }}"
- "Group: {{ script.stat.gr_name }}"
- "Modified: {{ script.stat.mtime }}"
when: script.stat.existsChecksum Verification
- stat:
path: /opt/myapp/app.jar
checksum_algorithm: sha256
register: app_file
- debug:
msg: "SHA256: {{ app_file.stat.checksum }}"
when: app_file.stat.exists
# Compare checksums
- fail:
msg: "File integrity check failed!"
when:
- app_file.stat.exists
- app_file.stat.checksum != expected_checksumDeployment Patterns
Skip if already deployed
- stat: { path: /opt/myapp-{{ version }}/app.jar }
register: deployed
- block:
- get_url:
url: "https://releases.example.com/app-{{ version }}.jar"
dest: /opt/myapp-{{ version }}/app.jar
- service: name=myapp state=restarted
when: not deployed.stat.exists
become: trueBackup before overwrite
- stat: { path: /etc/nginx/nginx.conf }
register: nginx_conf
- copy:
src: /etc/nginx/nginx.conf
dest: "/etc/nginx/nginx.conf.bak.{{ ansible_date_time.epoch }}"
remote_src: true
when: nginx_conf.stat.exists
become: trueFile age check
- stat: { path: /tmp/cache.json }
register: cache
- set_fact:
cache_expired: "{{ (ansible_date_time.epoch | int) - (cache.stat.mtime | int) > 3600 }}"
when: cache.stat.exists
- command: /opt/refresh-cache.sh
when: not cache.stat.exists or cache_expired | default(true)Windows Equivalent
- ansible.windows.win_stat:
path: C:\Program Files\MyApp\config.xml
register: win_file
- debug: msg="Config exists"
when: win_file.stat.existsFAQ
Does stat fail if file doesn't exist?
No — stat always succeeds. Check result.stat.exists for existence.
stat vs find?
stat checks a single known path. find searches for files matching patterns, size, or age criteria.
How do I check a remote file from the controller?
stat runs on the remote host. For controller files, use delegate_to: localhost or lookup('file').
Check File Exists
- ansible.builtin.stat:
path: /etc/myapp/config.yml
register: config_file
- debug:
msg: "Config {{ 'exists' if config_file.stat.exists else 'missing' }}"Conditional Task
- stat:
path: /opt/myapp/.installed
register: installed
- name: Run installer only if not already installed
command: /opt/myapp/install.sh
when: not installed.stat.exists
become: trueCheck Multiple Files
- stat:
path: "{{ item }}"
loop:
- /etc/myapp/config.yml
- /opt/myapp/app.jar
- /var/log/myapp
register: file_checks
- debug:
msg: "MISSING: {{ item.item }}"
loop: "{{ file_checks.results }}"
when: not item.stat.existsCheck File Properties
- stat:
path: /opt/scripts/deploy.sh
register: script
- debug:
msg: |
Size: {{ script.stat.size }} bytes
Mode: {{ script.stat.mode }}
Owner: {{ script.stat.pw_name }}
Modified: {{ script.stat.mtime }}
Is executable: {{ script.stat.executable }}
when: script.stat.existsCreate If Missing
- stat:
path: /etc/myapp/config.yml
register: config
- template:
src: default-config.yml.j2
dest: /etc/myapp/config.yml
when: not config.stat.exists
become: trueCheck If File Is Empty
- stat:
path: /var/log/myapp/error.log
register: error_log
- debug:
msg: "Error log has content!"
when: error_log.stat.exists and error_log.stat.size > 0Check Symlink
- stat:
path: /opt/myapp/current
register: current_link
- debug:
msg: "Points to: {{ current_link.stat.lnk_source }}"
when: current_link.stat.exists and current_link.stat.islnkVerify Checksum
- stat:
path: /opt/myapp/app.jar
checksum_algorithm: sha256
register: current_file
- debug:
msg: "File needs updating"
when: not current_file.stat.exists or current_file.stat.checksum != expected_checksumFAQ
stat vs creates/removes?
creates/removes only work with command/shell modules. stat works everywhere and returns full metadata.
Why check before creating?
For idempotency when using command/shell. Modules like file, copy, and template are already idempotent — they check internally.
How to check on the controller (local)?
- stat: { path: /local/file }
delegate_to: localhost
register: local_fileRelated Articles
Category: installation
Watch the video: Ansible Check if File Exists: stat Module with when Conditional (Guide) — Video Tutorial