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.

- 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

ParameterTypeRequiredDescription
pathstringYesPath to the file or directory to delete
statestringYesMust 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

TaskBest Approach
Delete a known file/directoryfile with state: absent
Delete files matching a patternfind + file (loop)
Delete files by agefind (with age) + file
Delete entire directory treefile with state: absent on parent
Delete and recreate directoryTwo tasks: absent then directory

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.