Ansible fetch Module: Download Files from Remote Hosts (Complete Guide)
By Luca Berton · Published 2024-01-01 · Category: troubleshooting
Complete guide to Ansible fetch module (ansible.builtin.fetch). Download and retrieve files from remote hosts to local machine with flat, dest.

How to copy files from remote hosts with Ansible?
I'm going to show you a live Playbook with some simple Ansible code. I'm Luca Berton and welcome to today's episode of Ansible Pilot
See also: Ansible fetch Module: Copy Files from Remote Hosts to Control Node
Ansible copy files from remote hosts
Today we're talking about Ansible modulefetch.
The full name is ansible.builtin.fetch which means is part of the collection of modules "builtin" with ansible and shipped with it.
This module is pretty stable and out for years.
The purpose is to copy files from remote locations. Please note that the opposite is done by Ansible copy module.
Parameters
•dest _path_
• src _string_
• fail_on_missing _boolean_
• validate_checksum _boolean_
• flat _boolean_
The parameter list is pretty wide but I'll summarize the most useful.
The only required parameter is "dest" which specifies a directory to save the file into and the "src" specifies the source files in the remote hosts. It
must be a file, not a directory.
The "fail_on_missing" boolean is set to true so the task is going to fail if the file doesn't exist.
The file is going to be transferred and validate in the source and the destination with a checksum. If we don't want this behavior we could override with the "validate_checksum" option.
The "flat" option allows you to override the default behavior of appending hostname/path/to/file to the destination.
See also: Ansible Copy Multiple Files: fileglob Lookup & with_fileglob Examples
Demo
Let's jump in a real-life playbook to copy files from remote hosts with Ansible • _fetch.yml_---
- name: fetch module Playbook
hosts: all
become: true
vars:
log_file: "/var/log/messages"
dump_dir: "logs"
tasks:
- name: fetch log
ansible.builtin.fetch:
src: "{{ log_file }}"
dest: "{{ dump_dir }}"
Conclusion
Now you know how to Copy files to remote hosts with Ansible.See also: Ansible win_copy Module: Copy Files to Windows Hosts (ansible.windows.win_copy)
Advanced Fetch Examples
Download with flat directory structure
By default, fetch creates a directory tree like host/path/to/file. Use flat: true for simpler output:
- name: Download config (flat structure)
ansible.builtin.fetch:
src: /etc/nginx/nginx.conf
dest: /tmp/configs/{{ inventory_hostname }}_nginx.conf
flat: true
Collect logs from all servers
- name: Fetch application logs
ansible.builtin.fetch:
src: /var/log/myapp/app.log
dest: ./collected-logs/
# Creates: ./collected-logs/server1/var/log/myapp/app.log
# ./collected-logs/server2/var/log/myapp/app.log
Download and verify checksum
- name: Fetch binary with validation
ansible.builtin.fetch:
src: /opt/myapp/bin/myapp
dest: ./downloads/
validate_checksum: true # Verify file integrity after download
Fetch only if file exists
- name: Check if log exists
ansible.builtin.stat:
path: /var/log/myapp/error.log
register: error_log
- name: Fetch error log if present
ansible.builtin.fetch:
src: /var/log/myapp/error.log
dest: ./error-logs/{{ inventory_hostname }}_error.log
flat: true
when: error_log.stat.exists
Collect SSL certificates for audit
- name: Fetch SSL certificates
ansible.builtin.fetch:
src: "{{ item }}"
dest: ./ssl-audit/{{ inventory_hostname }}/
flat: false
loop:
- /etc/ssl/certs/myapp.crt
- /etc/ssl/private/myapp.key
become: true
fetch vs synchronize vs slurp
| Module | Direction | Best For |
|--------|-----------|----------|
| fetch | Remote → Controller | Single files, structured collection |
| synchronize | Both directions | Large directories, rsync-based |
| slurp | Remote → Variable | Small files, read into memory |
| copy | Controller → Remote | Upload files |
Key Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| src | string | Remote file path |
| dest | string | Local destination directory |
| flat | bool | Don't create host/path subdirectories |
| validate_checksum | bool | Verify integrity after download |
| fail_on_missing | bool | Fail if source file doesn't exist |
FAQ
Can I fetch entire directories?
No — fetch only works with single files. For directories, use synchronize or create an archive first:
- name: Archive directory
ansible.builtin.archive:
path: /var/log/myapp/
dest: /tmp/myapp-logs.tar.gz
become: true
- name: Fetch archive
ansible.builtin.fetch:
src: /tmp/myapp-logs.tar.gz
dest: ./logs/{{ inventory_hostname }}_logs.tar.gz
flat: true
Why is flat: true important?
Without flat, fetching /etc/nginx.conf from server1 creates dest/server1/etc/nginx.conf. With flat: true, it saves directly to dest/filename. Use flat when you're building your own path with inventory_hostname.
Basic Fetch
- name: Download remote config
ansible.builtin.fetch:
src: /etc/myapp/config.yml
dest: ./collected-configs/
# Creates: ./collected-configs/web1/etc/myapp/config.yml
Flat Mode (No Host Directory)
- ansible.builtin.fetch:
src: /etc/hostname
dest: "./hostnames/{{ inventory_hostname }}.txt"
flat: true
# Creates: ./hostnames/web1.txt
Collect Logs from All Hosts
- name: Collect application logs
ansible.builtin.fetch:
src: /var/log/myapp/app.log
dest: "./logs/{{ inventory_hostname }}/"
flat: true
ignore_errors: true # Skip if file missing on some hosts
Fetch Before Upgrade (Backup)
---
- name: Backup and upgrade
hosts: webservers
tasks:
- name: Backup current config
ansible.builtin.fetch:
src: /etc/nginx/nginx.conf
dest: "./backups/{{ inventory_hostname }}/nginx.conf.{{ ansible_date_time.iso8601_basic_short }}"
flat: true
- name: Deploy new config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
become: true
notify: restart nginx
Fetch and Process
- fetch:
src: /opt/myapp/metrics.json
dest: /tmp/metrics/{{ inventory_hostname }}.json
flat: true
- name: Parse collected metrics
set_fact:
all_metrics: "{{ all_metrics | default([]) + [lookup('file', '/tmp/metrics/' + item + '.json') | from_json] }}"
loop: "{{ groups['appservers'] }}"
run_once: true
delegate_to: localhost
Fetch Multiple Files
# Find files first, then fetch
- find:
paths: /var/log/myapp
patterns: "*.log"
age: -1d # Modified in last day
register: recent_logs
- fetch:
src: "{{ item.path }}"
dest: "./logs/{{ inventory_hostname }}/"
flat: true
loop: "{{ recent_logs.files }}"
fetch vs copy vs synchronize
| Module | Direction | Use Case |
|--------|-----------|----------|
| fetch | Remote → Controller | Download files |
| copy | Controller → Remote | Upload files |
| synchronize | Both (rsync) | Large files, directories |
| slurp | Remote → Variable | Read file content |
Key Parameters
| Parameter | Description |
|-----------|-------------|
| src | Remote file path |
| dest | Local destination path |
| flat | Remove hostname/path prefix |
| validate_checksum | Verify integrity (default: true) |
| fail_on_missing | Fail if src doesn't exist (default: true) |
FAQ
Can fetch download directories?
No — fetch only works with single files. Use synchronize (rsync wrapper) or find + fetch loop for directories.
Why is the path so deep without flat?
By default, fetch preserves the full path including hostname to prevent collisions when fetching from multiple hosts.
Can I fetch binary files?
Yes — fetch handles binary files correctly. It uses checksums to verify integrity.
Basic Fetch
- ansible.builtin.fetch:
src: /var/log/myapp/app.log
dest: /tmp/collected-logs/
# Saves to /tmp/collected-logs/web1/var/log/myapp/app.log
Flat Mode (No Host Directory)
- fetch:
src: /etc/myapp/config.yml
dest: "/tmp/configs/{{ inventory_hostname }}-config.yml"
flat: true
# Saves to /tmp/configs/web1-config.yml
Collect from All Hosts
- hosts: all
tasks:
- fetch:
src: /var/log/syslog
dest: /tmp/syslogs/
# Creates:
# /tmp/syslogs/web1/var/log/syslog
# /tmp/syslogs/web2/var/log/syslog
# /tmp/syslogs/db1/var/log/syslog
Fetch with Validation
- fetch:
src: /opt/myapp/data.json
dest: /tmp/data/{{ inventory_hostname }}.json
flat: true
validate_checksum: true # Default: true
fail_on_missing: true # Default: true
Collect Backups
# Create backup on remote
- command: pg_dump mydb -f /tmp/db-backup.sql
become: true
become_user: postgres
# Download to controller
- fetch:
src: /tmp/db-backup.sql
dest: "/backups/{{ ansible_date_time.date }}/{{ inventory_hostname }}.sql"
flat: true
# Cleanup remote
- file:
path: /tmp/db-backup.sql
state: absent
Fetch vs copy vs synchronize
| Module | Direction | Use Case |
|--------|-----------|----------|
| copy | Controller → Remote | Upload files |
| fetch | Remote → Controller | Download files |
| synchronize | Both (rsync) | Bulk file sync |
| slurp | Remote → Variable | Read file content |
Fetch Multiple Files (with find)
- find:
paths: /var/log/myapp
patterns: "*.log"
register: log_files
- fetch:
src: "{{ item.path }}"
dest: /tmp/logs/
loop: "{{ log_files.files }}"
Fetch Binary Files
# fetch handles binary files automatically
- fetch:
src: /opt/myapp/database.db
dest: /tmp/db-snapshots/{{ inventory_hostname }}.db
flat: true
FAQ
Can I fetch directories?
No — fetch only works with single files. Use synchronize (rsync) or create a tarball first, then fetch.
What about large files?
fetch loads the entire file — for very large files, use synchronize or command: scp/rsync.
flat: true vs default?
Default creates dest/hostname/full/path/file. flat: true saves directly to dest (you must make filenames unique yourself).
Related Articles
• become and privilege escalation explained • role-based playbook organization in AnsibleCategory: troubleshooting
Watch the video: Ansible fetch Module: Download Files from Remote Hosts (Complete Guide) — Video Tutorial