AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

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.

Ansible fetch Module: Download Files from Remote Hosts (Complete Guide)

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 module fetch. 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 }}"

code with ❤️ in GitHub

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 explainedrole-based playbook organization in Ansible

Category: troubleshooting

Watch the video: Ansible fetch Module: Download Files from Remote Hosts (Complete Guide) — Video Tutorial

Browse all Ansible tutorials · AnsiblePilot Home