Ansible unarchive Module: Extract tar.gz, zip Archives on Remote Hosts
By Luca Berton · Published 2024-01-01 · Category: installation
How to extract archives with Ansible unarchive module (ansible.builtin.unarchive). Extract tar.gz, zip, bz2 from local or remote URLs.

The ansible.builtin.unarchive module extracts compressed archives (tar, tar.gz, tar.bz2, tar.xz, zip) on remote hosts. It copies from the Ansible controller or downloads from a URL, extracts to a target directory, and handles permissions and ownership — all in a single task.
How to Extract an Archive in Ansible?
I'm going to show you a live Playbook and some simple Ansible code. I'm Luca Berton and welcome to today's episode of Ansible Pilot.
The full name is ansible.builtin.unarchive, part of the builtin collection shipped with Ansible. It's stable, works across Linux distributions, and handles .zip files using unzip as well as .tar, .tar.gz, .tar.bz2, .tar.xz, and .tar.zst files using gtar.
For Windows targets, use the community.windows.win_unzip module instead.
See also: Ansible win_unzip Module: Extract ZIP Archives on Windows (Guide)
Basic Usage
Extract Local Archive to Remote Host
By default, src is a path on the Ansible controller. The archive is copied to the remote host, then extracted:
- name: Extract application archive
ansible.builtin.unarchive:
src: files/myapp-2.1.0.tar.gz
dest: /opt/myapp/
owner: appuser
group: appuser
mode: '0755'
become: true
Extract Archive Already on Remote Host
When the archive is already on the remote host, set remote_src: true:
- name: Extract remote archive
ansible.builtin.unarchive:
src: /tmp/myapp-2.1.0.tar.gz
dest: /opt/myapp/
remote_src: true
become: true
Download and Extract from URL
Set remote_src: true with an HTTP/HTTPS URL as src to download and extract in one step:
- name: Download and extract from URL
ansible.builtin.unarchive:
src: https://github.com/prometheus/prometheus/releases/download/v2.52.0/prometheus-2.52.0.linux-amd64.tar.gz
dest: /opt/
remote_src: true
become: true
Parameters Reference
| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| src | string | Archive path (local, remote, or URL) | Required |
| dest | string | Target extraction directory | Required |
| remote_src | boolean | Source is on remote host or URL | false |
| owner | string | Set owner of extracted files | — |
| group | string | Set group of extracted files | — |
| mode | string | Set permissions of extracted files | — |
| creates | string | Skip if this path exists (idempotency) | — |
| exclude | list | Files/dirs to exclude from extraction | — |
| include | list | Files/dirs to include in extraction | — |
| keep_newer | boolean | Don't overwrite newer files on remote | false |
| extra_opts | list | Extra command-line options for tar/unzip | — |
| list_files | boolean | Return list of extracted files in result | false |
| validate_certs | boolean | Validate SSL certs for HTTPS URLs | true |
See also: Ansible Delete File & Remove File: file Module absent State Guide
Idempotent Extraction
Using creates Parameter
The creates parameter is the recommended way to make extraction idempotent:
- name: Extract application (idempotent)
ansible.builtin.unarchive:
src: https://github.com/prometheus/prometheus/releases/download/v2.52.0/prometheus-2.52.0.linux-amd64.tar.gz
dest: /opt/
remote_src: true
creates: /opt/prometheus-2.52.0.linux-amd64/prometheus
Using stat + when Conditional
For more control, check with stat before extracting:
- name: Check if already extracted
ansible.builtin.stat:
path: /opt/myapp/bin/myapp
register: app_binary
- name: Extract application
ansible.builtin.unarchive:
src: files/myapp-{{ app_version }}.tar.gz
dest: /opt/myapp/
when: not app_binary.stat.exists
Include and Exclude Files
Extract Only Specific Files
- name: Extract only config files
ansible.builtin.unarchive:
src: files/myapp.tar.gz
dest: /opt/myapp/
include:
- 'myapp/config/*'
- 'myapp/README.md'
Exclude Files from Extraction
- name: Extract without tests and docs
ansible.builtin.unarchive:
src: files/myapp.tar.gz
dest: /opt/myapp/
exclude:
- 'myapp/tests/*'
- 'myapp/docs/*'
- '*.md'
See also: Ansible get_url Module Complete Reference: Checksums, Auth, Proxies & Retries
Strip Top-Level Directory
Many archives contain a top-level directory (e.g., myapp-2.1.0/). Use extra_opts to strip it:
- name: Extract without top-level directory
ansible.builtin.unarchive:
src: files/myapp-2.1.0.tar.gz
dest: /opt/myapp/
extra_opts:
- --strip-components=1
# Archive: myapp-2.1.0/bin/app → extracts as /opt/myapp/bin/app
Real-World Deployment Patterns
Deploy Application from GitHub Release
- name: Deploy Prometheus
hosts: monitoring
become: true
vars:
prometheus_version: "2.52.0"
prometheus_dir: /opt/prometheus
tasks:
- name: Create prometheus user
ansible.builtin.user:
name: prometheus
system: true
shell: /usr/sbin/nologin
create_home: false
- name: Create directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: prometheus
group: prometheus
loop:
- "{{ prometheus_dir }}"
- /var/lib/prometheus
- name: Download and extract Prometheus
ansible.builtin.unarchive:
src: "https://github.com/prometheus/prometheus/releases/download/v{{ prometheus_version }}/prometheus-{{ prometheus_version }}.linux-amd64.tar.gz"
dest: "{{ prometheus_dir }}"
remote_src: true
owner: prometheus
group: prometheus
extra_opts:
- --strip-components=1
creates: "{{ prometheus_dir }}/prometheus"
notify: restart prometheus
Blue-Green Deployment with Symlink
- name: Download release
ansible.builtin.get_url:
url: "https://releases.example.com/myapp-{{ app_version }}.tar.gz"
dest: "/tmp/myapp-{{ app_version }}.tar.gz"
checksum: "sha256:{{ release_checksum }}"
- name: Create release directory
ansible.builtin.file:
path: "/opt/releases/{{ app_version }}"
state: directory
owner: deploy
- name: Extract release
ansible.builtin.unarchive:
src: "/tmp/myapp-{{ app_version }}.tar.gz"
dest: "/opt/releases/{{ app_version }}/"
remote_src: true
owner: deploy
extra_opts:
- --strip-components=1
creates: "/opt/releases/{{ app_version }}/bin/app"
- name: Update symlink to new release
ansible.builtin.file:
src: "/opt/releases/{{ app_version }}"
dest: /opt/myapp/current
state: link
force: true
notify: restart myapp
Extract Multiple Archives in a Loop
- name: Extract tool archives
ansible.builtin.unarchive:
src: "{{ item.url }}"
dest: "{{ item.dest }}"
remote_src: true
creates: "{{ item.creates }}"
loop:
- url: https://example.com/tool1.tar.gz
dest: /opt/tool1/
creates: /opt/tool1/bin/tool1
- url: https://example.com/tool2.zip
dest: /opt/tool2/
creates: /opt/tool2/tool2
Video Playbook Example
---
- name: unarchive module Playbook
hosts: all
become: false
vars:
myurl: "https://github.com/lucab85/ansible-pilot/archive/refs/heads/master.zip"
tasks:
- name: Ensure extractors are present
ansible.builtin.yum:
name:
- unzip
- tar
state: present
become: true
- name: Extract archive from URL
ansible.builtin.unarchive:
src: "{{ myurl }}"
dest: "/home/devops/"
remote_src: true
validate_certs: true
Windows: win_unzip
- name: Extract ZIP on Windows
community.windows.win_unzip:
src: C:\temp\app.zip
dest: C:\Program Files\MyApp
creates: C:\Program Files\MyApp\app.exe
Supported Archive Formats
| Format | Extension | Requirement |
|--------|-----------|-------------|
| tar | .tar | tar |
| tar + gzip | .tar.gz, .tgz | tar + gzip |
| tar + bzip2 | .tar.bz2 | tar + bzip2 |
| tar + xz | .tar.xz | tar + xz |
| tar + zstd | .tar.zst | tar + zstd |
| zip | .zip | unzip + zipinfo |
> Note: The remote host needs the appropriate tools installed. tar and gzip are usually pre-installed on Linux. For zip archives, install unzip first.
FAQ
How do I extract a tar.gz file with Ansible?
Use the unarchive module with src pointing to the archive and dest to the target directory. For local archives: ansible.builtin.unarchive: src=files/app.tar.gz dest=/opt/app/. For archives on the remote host, add remote_src: true. For URLs, use remote_src: true with the URL as src.
How do I download and extract in one step?
Set remote_src: true and use an HTTP/HTTPS URL as src. Ansible downloads and extracts in a single task without intermediate steps.
How do I make unarchive idempotent?
Use the creates parameter pointing to a file that exists after extraction: creates: /opt/app/bin/myapp. If that file exists, the task is skipped entirely.
How do I strip the top-level directory from an archive?
Use extra_opts: ['--strip-components=1']. This removes the first directory level, so myapp-1.0/bin/app extracts as bin/app in the destination directory.
What is the difference between remote_src true and false?
With remote_src: false (default), src is a path on the Ansible controller — the archive is copied to the remote host then extracted. With remote_src: true, src is a path already on the remote host or a URL to download from.
Why does unarchive report "changed" every time?
The module checks file timestamps. Use creates to skip if already extracted, or use stat + when for more precise conditional extraction.
How do I extract a single .gz file (not tar.gz)?
Single gzip files aren't archives. Use the command module: command: gunzip /tmp/file.gz. The unarchive module only handles archive formats containing multiple files.
How do I extract password-protected archives?
Not supported natively by the unarchive module. Use command: unzip -P {{ password }} archive.zip -d /dest with no_log: true to prevent password leaking to logs.
"unzip not found" error?
Install unzip on the remote host first: ansible.builtin.package: name=unzip state=present. This is only needed for .zip files — tar archives don't require extra packages.
Conclusion
The ansible.builtin.unarchive module is essential for deploying applications from archives:
• src + dest — Extract archive to target directory
• remote_src: true — Archive is on remote host or a URL
• creates: — Make extraction idempotent
• extra_opts: [--strip-components=1] — Remove top-level directory
• owner/group/mode — Set file ownership and permissions
• include/exclude — Extract specific files only
• Windows: Use community.windows.win_unzip instead
Related Articles
• Ansible copy Module: Copy Files to Remote Hosts • Ansible get_url Module: Download Files • Ansible file Module: Create Files and Directories • Ansible Become Guide: Privilege EscalationSee also
• Ansible unarchive Module: Extract tar, zip, gz Archives (Complete Guide)Category: installation
Watch the video: Ansible unarchive Module: Extract tar.gz, zip Archives on Remote Hosts — Video Tutorial