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 copy Module: Copy Files to Remote Hosts (ansible.builtin.copy Guide)

By Luca Berton · Published 2024-01-01 · Category: windows-automation

How to copy files to remote hosts with Ansible copy module (ansible.builtin.copy). Transfer files, set permissions, use content parameter, backup.

Ansible copy Module: Copy Files to Remote Hosts (ansible.builtin.copy Guide)

How to copy files to remote hosts?

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 Copy Multiple Files: fileglob Lookup & with_fileglob Examples

Ansible copy files to remote hosts

Today we're talking about Ansible module copy. The full name is "ansible.builtin.copy" 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 to remote locations. Please note that the opposite is done by Ansible fetch module. For Windows target use Ansible win_copy module.

Parameters

• dest _path_ - remote path • src _string_ - local path • backup _boolean_ - no / yes • validate _string_ - validation command • checksum _string_ 2.5+ • mode/owner/group • setype/seuser/selevel

The parameter list is pretty wide but I'll summarize the most useful.

The only required parameter is "dest" which specifies the remote absolute path destination.

The "src" specifies the source file in the controller host. It could be a relative or absolute path. I recommend absolutely.

The backup boolean option allows you to create a backup if the utility overwrites any file.

If there is any tool to validate the file we could specify it in the validate parameter, very useful for configuration files.

Let me also highlight that we could also specify the permissions and SELinux properties.

See also: Ansible win_copy Module: Copy Files to Windows Hosts (ansible.windows.win_copy)

Demo

Let's jump in a real-life playbook to copy files to remote hosts with Ansible.

code

• copy.yml
---
- name: copy module Playbook
  hosts: all
  become: false
  tasks:
    - name: copy report.txt
      ansible.builtin.copy:
        src: report.txt
        dest: /home/devops/report.txt
        owner: devops
        mode: '0644'
• report.txt
test report.txt

code with ❤️ in GitHub

Conclusion

Now you know how to Copy files to remote hosts with Ansible.

See also: Ansible Create File with Content: copy Module content Parameter

Advanced Copy Examples

Copy with backup

- name: Deploy config with automatic backup
  ansible.builtin.copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    backup: true  # Saves existing file as .backup
  become: true
  notify: reload nginx

Copy entire directory

- name: Copy directory to remote
  ansible.builtin.copy:
    src: files/app/    # Trailing slash copies CONTENTS
    dest: /opt/myapp/
    owner: appuser
    group: appuser
    mode: '0755'
  become: true

# Without trailing slash: copies the directory itself # src: files/app → creates /opt/myapp/app/ # src: files/app/ → copies contents into /opt/myapp/

Validate before replacing

- name: Copy and validate nginx config
  ansible.builtin.copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    validate: /usr/sbin/nginx -t -c %s
  become: true
  # Only replaces if nginx -t passes

Copy with content from variable

- name: Generate hosts file entry
  ansible.builtin.copy:
    content: |
      {% for host in groups['webservers'] %}
      {{ hostvars[host].ansible_host }} {{ host }}
      {% endfor %}
    dest: /etc/hosts.d/webservers
    mode: '0644'
  become: true

Remote-to-remote copy (using remote_src)

- name: Copy file on remote host
  ansible.builtin.copy:
    src: /var/log/app.log
    dest: /backup/app.log.bak
    remote_src: true  # Source is on the remote host
  become: true

copy vs synchronize vs template

| Module | Use Case | Speed | |--------|----------|-------| | copy | Single files, small directories | Moderate | | synchronize | Large directories (rsync) | Fast | | template | Files needing Jinja2 rendering | Moderate | | fetch | Remote → controller (reverse copy) | Moderate |

For directories with many files, synchronize (rsync) is significantly faster than copy.

Key Parameters

| Parameter | Description | |-----------|-------------| | src | Local source path | | dest | Remote destination path | | content | Write text content directly | | mode | File permissions | | owner / group | File ownership | | backup | Create backup of existing file | | force | Overwrite if content differs (default: true) | | remote_src | Source is on remote host | | validate | Command to validate file before replacing | | directory_mode | Permissions for created directories |

FAQ

Why is copy slow for large files?

Ansible copy calculates checksums and transfers via SSH/SFTP. For large files or many files, use synchronize (rsync) instead:

- name: Sync large directory
  ansible.posix.synchronize:
    src: /local/large-dir/
    dest: /remote/large-dir/

How do I copy files between two remote hosts?

Use fetch + copy, or delegate:

- name: Fetch from source
  ansible.builtin.fetch:
    src: /data/export.csv
    dest: /tmp/
    flat: true
  delegate_to: source_server

- name: Copy to destination ansible.builtin.copy: src: /tmp/export.csv dest: /data/import.csv

Does copy preserve timestamps?

No — copy sets the modification time to the current time. Use synchronize with --times to preserve timestamps.

Copy File to Remote

- name: Copy config to remote
  ansible.builtin.copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
  become: true

Copy with Backup

- copy:
    src: files/app.conf
    dest: /etc/myapp/app.conf
    backup: true  # Creates timestamped backup
  become: true

Copy Directory

# With trailing slash — copies CONTENTS
- copy:
    src: files/configs/
    dest: /etc/myapp/
  become: true

# Without trailing slash — copies the DIRECTORY itself - copy: src: files/configs dest: /opt/ become: true

Copy Between Remote Paths

- copy:
    src: /opt/myapp/config.yml
    dest: /opt/myapp/config.yml.bak
    remote_src: true
  become: true

Inline Content

- copy:
    content: |
      DB_HOST={{ db_host }}
      DB_PORT={{ db_port }}
      APP_ENV={{ env }}
    dest: /etc/myapp/.env
    mode: '0600'
  become: true

Copy Multiple Files

- copy:
    src: "{{ item.src }}"
    dest: "{{ item.dest }}"
    mode: "{{ item.mode | default('0644') }}"
  loop:
    - { src: nginx.conf, dest: /etc/nginx/nginx.conf }
    - { src: php.ini, dest: /etc/php/8.2/fpm/php.ini }
    - { src: deploy.sh, dest: /opt/scripts/deploy.sh, mode: '0755' }
  become: true
  notify: restart services

Conditional Copy

- copy:
    src: "configs/{{ env }}.conf"
    dest: /etc/myapp/config.conf
  become: true

# Or with when - copy: src: files/ssl.conf dest: /etc/nginx/conf.d/ssl.conf when: enable_ssl | default(false) become: true

Validate Before Placing

- copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    validate: "nginx -t -c %s"
  become: true

- copy: src: files/sudoers dest: /etc/sudoers validate: "visudo -cf %s" become: true

Force and Idempotency

# Only copy if file doesn't exist
- copy:
    src: files/initial-config.yml
    dest: /etc/myapp/config.yml
    force: false  # Don't overwrite existing
  become: true

# Always overwrite (default) - copy: src: files/config.yml dest: /etc/myapp/config.yml force: true become: true

copy vs synchronize vs template

| Module | Best For | |--------|----------| | copy | Single/few files, inline content | | synchronize | Large directory trees (rsync) | | template | Files with Jinja2 variables/logic | | fetch | Copy FROM remote TO controller | | get_url | Download from HTTP/HTTPS |

Key Parameters

| Parameter | Description | |-----------|-------------| | src | Source file/dir on controller | | dest | Destination on remote | | content | Inline content (instead of src) | | owner/group | File ownership | | mode | Permissions | | backup | Keep backup of original | | force | Overwrite if different | | remote_src | Source is on remote host | | validate | Validation command | | directory_mode | Mode for created directories | | follow | Follow symlinks |

FAQ

How do I copy from remote to local?

Use the fetch module:

- fetch:
    src: /var/log/app.log
    dest: /tmp/logs/{{ inventory_hostname }}/
    flat: true

Large files are slow — alternatives?

Use synchronize (rsync wrapper) for large files or directories — it's significantly faster.

Why does copy show "changed" every run?

Check file permissions — if mode doesn't match, Ansible reports a change. Always specify mode explicitly.

Copy a File

- ansible.builtin.copy:
    src: files/app.conf
    dest: /etc/myapp/app.conf
  become: true

With Permissions

- copy:
    src: files/app.conf
    dest: /etc/myapp/app.conf
    owner: appuser
    group: appgroup
    mode: '0644'
  become: true

Write Content Directly

- copy:
    content: |
      [database]
      host = db.example.com
      port = 5432
    dest: /etc/myapp/db.conf
    mode: '0644'
  become: true

Copy Directory

- copy:
    src: files/configs/   # Trailing slash = copy contents
    dest: /etc/myapp/
  become: true

- copy: src: files/configs # No trailing slash = copy directory itself dest: /etc/myapp/ become: true

Backup Before Overwrite

- copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    backup: true  # Creates .bak file
  become: true
  notify: reload nginx

Don't Overwrite Existing

- copy:
    src: files/default.conf
    dest: /etc/myapp/config.conf
    force: false  # Skip if dest exists
  become: true

Validate Before Deploy

# Validate nginx config
- copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    validate: "nginx -t -c %s"
  become: true

# Validate sudoers - copy: src: files/sudoers dest: /etc/sudoers.d/myapp validate: "visudo -cf %s" mode: '0440' become: true

Copy from Remote to Remote

# remote_src copies on the remote host itself
- copy:
    src: /opt/backup/config.conf
    dest: /etc/myapp/config.conf
    remote_src: true
  become: true

Copy with Variable Content

- copy:
    content: "{{ lookup('template', 'config.j2') }}"
    dest: /etc/myapp/config.conf
  become: true

copy vs template vs synchronize

| Module | Use Case | |--------|----------| | copy | Static files, small content | | template | Dynamic files with Jinja2 | | synchronize | Large directories (rsync) | | fetch | Remote → controller (reverse copy) |

FAQ

copy vs fetch?

copy sends files controller→remote. fetch pulls files remote→controller.

How to copy to multiple hosts?

Just target multiple hosts — copy runs on each host in the play.

Large files slow?

Use synchronize (rsync wrapper) for large files or many files — it's much faster than copy.

Related Articles

the Ansible become referencethe Ansible roles overviewAnsible for Windows guide

Category: windows-automation

Watch the video: Ansible copy Module: Copy Files to Remote Hosts (ansible.builtin.copy Guide) — Video Tutorial

Browse all Ansible tutorials · AnsiblePilot Home