Introduction
Ansible unarchive (ansible.builtin.unarchive) is the module for extracting compressed archives — .tar.gz, .zip, .tar.bz2, .tar.xz — onto a remote host. It can unpack an archive already on the control node, one already present on the remote host (remote_src: true), or download it directly from a URL and extract it in one task.
Basic Usage
# Extract local archive to remote host
- name: Extract app archive
ansible.builtin.unarchive:
src: files/myapp-2.1.0.tar.gz
dest: /opt/myapp/
# Extract archive already on the remote host
- name: Extract remote archive
ansible.builtin.unarchive:
src: /tmp/myapp-2.1.0.tar.gz
dest: /opt/myapp/
remote_src: true
# Download from URL and extract
- name: Download and extract
ansible.builtin.unarchive:
src: https://github.com/myorg/myapp/releases/download/v2.1.0/myapp-2.1.0.tar.gz
dest: /opt/myapp/
remote_src: true
Parameters
| Parameter | Default | Description |
|---|---|---|
src | (required) | Archive path or URL |
dest | (required) | Extraction destination |
remote_src | false | Archive is on remote host (or URL) |
owner | — | Owner of extracted files |
group | — | Group of extracted files |
mode | — | Permissions of extracted files |
creates | — | Skip if this path exists (idempotency) |
exclude | — | List of files/dirs to exclude |
include | — | List of files/dirs to include (extract only these) |
extra_opts | — | Extra options for tar/unzip command |
keep_newer | false | Don't overwrite newer files on remote |
list_files | false | Return list of extracted files |
validate_certs | true | Validate SSL when downloading URLs |
Common Patterns
Download and Install Software
- name: Download Node.js
ansible.builtin.unarchive:
src: "https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-x64.tar.xz"
dest: /usr/local/
remote_src: true
creates: /usr/local/node-v20.11.0-linux-x64
extra_opts:
- --strip-components=1 # Remove top-level directory
- name: Download and extract Go
ansible.builtin.unarchive:
src: https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
dest: /usr/local/
remote_src: true
creates: /usr/local/go/bin/go
Deploy Application Release
- name: Create release directory
ansible.builtin.file:
path: "/opt/myapp/releases/{{ version }}"
state: directory
owner: deploy
group: deploy
- name: Extract release
ansible.builtin.unarchive:
src: "https://releases.example.com/myapp-{{ version }}.tar.gz"
dest: "/opt/myapp/releases/{{ version }}"
remote_src: true
owner: deploy
group: deploy
creates: "/opt/myapp/releases/{{ version }}/bin/myapp"
- name: Switch symlink to new release
ansible.builtin.file:
src: "/opt/myapp/releases/{{ version }}"
dest: /opt/myapp/current
state: link
force: true
notify: Restart myapp
Extract Specific Files Only
# Extract only config files from archive
- name: Extract config only
ansible.builtin.unarchive:
src: /tmp/myapp-2.1.0.tar.gz
dest: /opt/myapp/
remote_src: true
include:
- "myapp-2.1.0/config/"
- "myapp-2.1.0/config.yml"
# Exclude test files
- name: Extract without tests
ansible.builtin.unarchive:
src: /tmp/myapp-2.1.0.tar.gz
dest: /opt/myapp/
remote_src: true
exclude:
- "*/tests/*"
- "*/docs/*"
- "*.md"
Extract and List Files
- name: Extract and list contents
ansible.builtin.unarchive:
src: /tmp/archive.tar.gz
dest: /opt/myapp/
remote_src: true
list_files: true
register: extracted
- name: Show extracted files
ansible.builtin.debug:
msg: "Extracted {{ extracted.files | length }} files"
Strip Top-Level Directory
Many archives contain a top-level directory (e.g., myapp-2.1.0/). Use extra_opts to strip it:
# Archive structure: myapp-2.1.0/bin/myapp, myapp-2.1.0/config.yml
# You want: /opt/myapp/bin/myapp, /opt/myapp/config.yml
- name: Extract without top-level directory
ansible.builtin.unarchive:
src: /tmp/myapp-2.1.0.tar.gz
dest: /opt/myapp/
remote_src: true
extra_opts:
- --strip-components=1
Zip Files
# Extract zip (requires unzip on remote)
- name: Ensure unzip is installed
ansible.builtin.apt:
name: unzip
state: present
- name: Extract zip archive
ansible.builtin.unarchive:
src: /tmp/webapp.zip
dest: /var/www/html/
remote_src: true
owner: www-data
group: www-data
Idempotency with creates
Without creates, the module extracts every run. Use creates to skip if already extracted:
# Only extract if /opt/myapp/bin/myapp doesn't exist
- name: Extract once
ansible.builtin.unarchive:
src: /tmp/myapp-2.1.0.tar.gz
dest: /opt/myapp/
remote_src: true
creates: /opt/myapp/bin/myapp
Full Playbook Example
---
- name: Install Prometheus
hosts: monitoring
become: true
vars:
prometheus_version: "2.50.0"
prometheus_url: "https://github.com/prometheus/prometheus/releases/download/v{{ prometheus_version }}/prometheus-{{ prometheus_version }}.linux-amd64.tar.gz"
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:
- /opt/prometheus
- /etc/prometheus
- /var/lib/prometheus
- name: Download and extract Prometheus
ansible.builtin.unarchive:
src: "{{ prometheus_url }}"
dest: /opt/prometheus/
remote_src: true
owner: prometheus
group: prometheus
extra_opts:
- --strip-components=1
creates: /opt/prometheus/prometheus
- name: Symlink binaries
ansible.builtin.file:
src: "/opt/prometheus/{{ item }}"
dest: "/usr/local/bin/{{ item }}"
state: link
loop:
- prometheus
- promtool
- name: Deploy config
ansible.builtin.template:
src: prometheus.yml.j2
dest: /etc/prometheus/prometheus.yml
owner: prometheus
notify: Restart prometheus
- name: Enable and start
ansible.builtin.systemd:
name: prometheus
state: started
enabled: true
handlers:
- name: Restart prometheus
ansible.builtin.systemd:
name: prometheus
state: restarted
Troubleshooting
"Failed to find handler for" Archive
The remote host needs the right tool installed:
# For .zip files
- apt: { name: unzip, state: present }
# For .tar.xz files
- apt: { name: xz-utils, state: present }
# For .tar.bz2 files
- apt: { name: bzip2, state: present }
"Destination directory does not exist"
Create the destination first:
- file:
path: /opt/myapp
state: directory
- unarchive:
src: archive.tar.gz
dest: /opt/myapp/
Downloaded Archive Fails to Extract
Check SSL and URL:
- unarchive:
src: https://example.com/archive.tar.gz
dest: /opt/
remote_src: true
validate_certs: false # For self-signed certs
Related Articles
Conclusion
ansible.builtin.unarchive extracts .tar.gz, .zip, .tar.bz2, and .tar.xz archives. Set remote_src: true to extract archives already on the remote host or download from URLs. Use creates for idempotency, extra_opts: [--strip-components=1] to remove top-level directories, and include/exclude to extract selectively. Ensure the required extraction tool (unzip, xz-utils, bzip2) is installed on the target.