Introduction
Deleting files and directories is one of the most common operations in system automation — cleaning up temp files, removing old deployments, deleting stale configs, or purging log files. The ansible.builtin.file module with state: absent handles all of these, with Ansible's built-in idempotency ensuring safe repeated execution.
For Windows targets, use ansible.windows.win_file instead.
Basic Usage
Delete a File
- name: Delete a file
ansible.builtin.file:
path: /tmp/deleteme.txt
state: absent
If the file doesn't exist, the task succeeds with changed: false — this is idempotent by design.
Delete a Directory (and All Contents)
- name: Delete directory and everything inside
ansible.builtin.file:
path: /tmp/old-build
state: absent
This recursively removes the directory and all its contents — equivalent to rm -rf.
Delete a Symbolic Link
- name: Remove symlink
ansible.builtin.file:
path: /opt/app/current
state: absent
This removes the symlink itself, not the target it points to.
Module Parameters for Deletion
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Path to the file or directory to delete |
state | string | Yes | Must be absent for deletion |
That's it — deletion only needs path and state: absent. No owner, mode, or other attributes apply.
Practical Examples
Conditional Deletion
Delete only if a file exists and meets certain criteria:
- name: Check if old config exists
ansible.builtin.stat:
path: /etc/myapp/config.old
register: old_config
- name: Delete old config if it's older than 30 days
ansible.builtin.file:
path: /etc/myapp/config.old
state: absent
when:
- old_config.stat.exists
- (ansible_date_time.epoch | int - old_config.stat.mtime | int) > 2592000
Delete Multiple Files
- name: Delete temporary files
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /tmp/build-output.tar.gz
- /tmp/test-results.xml
- /var/log/myapp/debug.log
- /home/deploy/.bash_history
Clean Up Old Deployments
Keep the last 3 releases and delete the rest:
- name: List all releases
ansible.builtin.find:
paths: /opt/app/releases
file_type: directory
register: releases
- name: Sort by modification time
ansible.builtin.set_fact:
old_releases: "{{ releases.files | sort(attribute='mtime') | map(attribute='path') | list }}"
- name: Keep only the 3 most recent releases
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop: "{{ old_releases[:-3] }}"
when: old_releases | length > 3
Delete Files Matching a Pattern
Use find module to locate files, then delete them:
- name: Find log files older than 7 days
ansible.builtin.find:
paths: /var/log/myapp
patterns: "*.log"
age: 7d
register: old_logs
- name: Delete old log files
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ old_logs.files }}"
- name: Report cleanup
ansible.builtin.debug:
msg: "Deleted {{ old_logs.files | length }} old log files"
Delete Files by Size
- name: Find large temp files (>100MB)
ansible.builtin.find:
paths: /tmp
size: 100m
file_type: file
register: large_files
- name: Remove large temp files
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ large_files.files }}"
Pre-Deployment Cleanup
- name: Clean deployment
hosts: web_servers
become: true
tasks:
- name: Stop application
ansible.builtin.systemd:
name: myapp
state: stopped
- name: Remove old deployment
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /opt/myapp/app
- /opt/myapp/static
- /opt/myapp/tmp
- /var/cache/myapp
- name: Deploy new version
ansible.builtin.unarchive:
src: "myapp-{{ version }}.tar.gz"
dest: /opt/myapp/
- name: Start application
ansible.builtin.systemd:
name: myapp
state: started
Safe Deletion with Backup
Before deleting, back up the file:
- name: Backup before delete
ansible.builtin.fetch:
src: /etc/nginx/old-site.conf
dest: "./backups/{{ inventory_hostname }}/"
ignore_errors: true
- name: Delete old config
ansible.builtin.file:
path: /etc/nginx/old-site.conf
state: absent
Error Handling
Ignore Missing Files
state: absent already handles missing files gracefully — no error is raised. But if you need explicit handling:
- name: Delete optional file
ansible.builtin.file:
path: /tmp/maybe-exists.txt
state: absent
register: delete_result
- name: Report
ansible.builtin.debug:
msg: "File was {{ 'deleted' if delete_result.changed else 'already absent' }}"
Permission Errors
If the Ansible user doesn't have permission to delete:
- name: Delete protected file
ansible.builtin.file:
path: /root/sensitive.conf
state: absent
become: true # Escalate privileges
file (absent) vs Other Deletion Methods
| Task | Best Approach |
|---|---|
| Delete a known file/directory | file with state: absent |
| Delete files matching a pattern | find + file (loop) |
| Delete files by age | find (with age) + file |
| Delete entire directory tree | file with state: absent on parent |
| Delete and recreate directory | Two tasks: absent then directory |
Related Articles
- Create an Empty File: Ansible file Module
- Ansible File Module: Manage File Properties
- How to Check If a Directory Exists
- Copy Files to Remote Hosts
- Download Files from Remote Hosts: fetch
- Ansible lineinfile Module
- Ansible Best Practices Guide
Conclusion
Deleting files and directories with ansible.builtin.file and state: absent is straightforward and idempotent — it succeeds whether the target exists or not. For production automation, combine it with the find module for pattern-based and age-based cleanup, use fetch for pre-deletion backups, and always use become: true when deleting files owned by other users. The key patterns: conditional deletion with stat checks, batch cleanup with find loops, and release management keeping only the N most recent deployments.