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
| Parameter | Type | Required | Description |
|---|---|---|---|
src | path/URL | Yes | Archive source — local path, remote path, or URL |
dest | path | Yes | Directory to extract into |
remote_src | bool | No | Source is on the remote host (default: false) |
creates | path | No | Skip extraction if this path exists (idempotency) |
include | list | No | Only extract these files/directories |
exclude | list | No | Skip these files/directories |
extra_opts | list | No | Extra command-line options for the extractor |
keep_newer | bool | No | Don't replace newer files on remote |
validate_certs | bool | No | Validate SSL certs for URL sources (default: true) |
mode / owner / group | string | No | Set permissions on extracted files |
list_files | bool | No | Return list of extracted files |
Requirements
The remote host needs:
| Archive Type | Required Package |
|---|---|
.zip | unzip, zipinfo |
.tar, .tar.gz, .tar.bz2, .tar.xz | gtar (GNU tar) |
.tar.zst | gtar + 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
| Task | Module |
|---|---|
| Extract archive | unarchive |
| Create archive | community.general.archive |
| Download file (no extract) | get_url |
| Copy file | copy |
| Windows unzip | community.windows.win_unzip |
Related Articles
- Copy Files to Remote: copy Module
- Ansible Galaxy Guide
- Change File Permissions: file Module
- Install Packages: yum Module
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.