Introduction

The ansible.builtin.unarchive module extracts archives on remote hosts — handling .zip, .tar, .tar.gz, .tar.bz2, .tar.xz, and .tar.zst files. It can copy from the controller, extract files already on the remote, or download from a URL. This makes it the standard tool for deploying application packages, extracting backups, and distributing release artifacts.

Module Parameters

ParameterTypeRequiredDescription
srcpath/URLYesArchive source — local path, remote path, or URL
destpathYesDirectory to extract into
remote_srcboolNoSource is on the remote host (default: false)
createspathNoSkip extraction if this path exists (idempotency)
includelistNoOnly extract these files/directories
excludelistNoSkip these files/directories
extra_optslistNoExtra command-line options for the extractor
keep_newerboolNoDon't replace newer files on remote
validate_certsboolNoValidate SSL certs for URL sources (default: true)
mode / owner / groupstringNoSet permissions on extracted files
list_filesboolNoReturn list of extracted files

Requirements

The remote host needs:

Archive TypeRequired Package
.zipunzip, zipinfo
.tar, .tar.gz, .tar.bz2, .tar.xzgtar (GNU tar)
.tar.zstgtar + zstd

Basic Examples

Extract Local Archive to Remote

- name: Deploy application
  ansible.builtin.unarchive:
    src: app-v2.1.tar.gz
    dest: /opt/myapp/
    owner: myapp
    group: myapp
    mode: '0755'

This copies the archive from the controller to the remote, then extracts it.

Extract from URL

- name: Download and extract release
  ansible.builtin.unarchive:
    src: https://github.com/prometheus/prometheus/releases/download/v2.48.0/prometheus-2.48.0.linux-amd64.tar.gz
    dest: /opt/
    remote_src: true
    creates: /opt/prometheus-2.48.0.linux-amd64

Extract Archive Already on Remote

- name: Extract backup archive
  ansible.builtin.unarchive:
    src: /tmp/backup-2024-01-15.tar.gz
    dest: /var/restore/
    remote_src: true

Practical Patterns

Install Dependencies First

- name: Ensure extractors are installed
  ansible.builtin.yum:
    name:
      - unzip
      - tar
      - gzip
    state: present
  become: true

- name: Extract archive
  ansible.builtin.unarchive:
    src: "{{ archive_url }}"
    dest: /home/deploy/
    remote_src: true

Idempotent Extraction with creates

- name: Extract only if not already done
  ansible.builtin.unarchive:
    src: https://example.com/app-v3.0.tar.gz
    dest: /opt/
    remote_src: true
    creates: /opt/app-v3.0/bin/app

The creates parameter makes the task idempotent — it skips extraction if the specified path already exists.

Extract Specific Files

- name: Extract only config files
  ansible.builtin.unarchive:
    src: /tmp/release.tar.gz
    dest: /etc/myapp/
    remote_src: true
    include:
      - "config/"
      - "*.conf"
    exclude:
      - "*.log"
      - "tmp/"

List Extracted Files

- name: Extract and list files
  ansible.builtin.unarchive:
    src: app.tar.gz
    dest: /opt/app/
    list_files: true
  register: extract_result

- name: Show extracted files
  ansible.builtin.debug:
    var: extract_result.files

Deploy Java Application

- name: Deploy Tomcat
  ansible.builtin.unarchive:
    src: "https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.84/bin/apache-tomcat-9.0.84.tar.gz"
    dest: /opt/
    remote_src: true
    creates: /opt/apache-tomcat-9.0.84
    owner: tomcat
    group: tomcat

- name: Create symlink
  ansible.builtin.file:
    src: /opt/apache-tomcat-9.0.84
    dest: /opt/tomcat
    state: link

Extract with Extra Options

# Strip leading directory from tar archive
- name: Extract without top-level directory
  ansible.builtin.unarchive:
    src: app-v2.tar.gz
    dest: /opt/app/
    extra_opts:
      - --strip-components=1

# Extract with specific tar options
- name: Extract preserving permissions
  ansible.builtin.unarchive:
    src: backup.tar.gz
    dest: /var/restore/
    remote_src: true
    extra_opts:
      - --same-owner
      - --preserve-permissions

Complete Application Deployment

---
- name: Deploy application from release
  hosts: app_servers
  become: true
  vars:
    app_version: "2.1.0"
    app_url: "https://releases.example.com/app-{{ app_version }}.tar.gz"
    app_dir: "/opt/app"

  tasks:
    - name: Ensure dependencies
      ansible.builtin.yum:
        name: [unzip, tar]
        state: present

    - name: Create app directory
      ansible.builtin.file:
        path: "{{ app_dir }}"
        state: directory
        owner: app
        group: app
        mode: '0755'

    - name: Download and extract release
      ansible.builtin.unarchive:
        src: "{{ app_url }}"
        dest: "{{ app_dir }}"
        remote_src: true
        extra_opts: [--strip-components=1]
        owner: app
        group: app
        creates: "{{ app_dir }}/bin/app"
      notify: restart app

    - name: Deploy configuration
      ansible.builtin.template:
        src: app.conf.j2
        dest: "{{ app_dir }}/config/app.conf"
        owner: app
        group: app
        mode: '0640'
      notify: restart app

  handlers:
    - name: restart app
      ansible.builtin.systemd:
        name: myapp
        state: restarted

Common Errors

"Failed to find handler for"

fatal: [host]: FAILED! => {"msg": "Failed to find handler for \"/tmp/file.zip\".
Make sure the required command to extract the file is installed."}

Fix: Install unzip on the remote host:

- ansible.builtin.yum:
    name: unzip
    state: present

Permission Denied

fatal: [host]: FAILED! => {"msg": "Destination /opt/app not writable"}

Fix: Use become: true or ensure the user has write permissions.

Archive Already Extracted (Changed Every Run)

Use creates for idempotency:

- ansible.builtin.unarchive:
    src: app.tar.gz
    dest: /opt/
    creates: /opt/app/bin/start.sh  # Skip if this file exists

unarchive vs Other Modules

TaskModule
Extract archiveunarchive
Create archivecommunity.general.archive
Download file (no extract)get_url
Copy filecopy
Windows unzipcommunity.windows.win_unzip

Conclusion

Use ansible.builtin.unarchive with remote_src: true for URL downloads and remote archives, creates for idempotency, extra_opts: [--strip-components=1] to flatten directory structure, and include/exclude for selective extraction. Always ensure the required extractor (unzip, tar) is installed on the remote host first.