Table of Contents
Go from Ansible beginner to Ansible pro with this full video course.
What does the Ansible copy module do?
Ansible’s copy module is suited to the following tasks:
- Copying files from the local host to the remote host
- name: copy file from local host to remote host (relative path, ./files/)
  copy:
    src: test_file
    dest: $HOME/test_file
- name: copy file from local host to remote host (absolute path)
  copy:
    src: /path/to/ansible/files/test_file
    dest: $HOME/test_file
- Copying files between locations on the remote host (using the remote_srcparameter)
- name: copy file between locations on the remote host
  copy:
    src: $HOME/test_file
    remote_src: true
    dest: $HOME/test_file2
- Writing a simple templated text file (using the contentparameter)
- name: write templated text content into a file on the remote host
  copy:
    dest: $HOME/test_file
    content: "Hello, {{ ansible_user }}!"
If you need to copy files from the remote host to the local host, use the fetch module.
Ansible copy Module Video Tutorial
If you prefer watching to reading, here’s a full video tutorial from the TopTechSkills YouTube channel covering a many of the points and examples from this article. Feel free to comment on this article or the video if you have any questions.
When to use the copy module vs synchronize module
If you need to copy many (> 100) deeply nested files recursively, I recommend you use the synchronize module since recursively copying many files with copy is slow. The synchronize module is a wrapper around rsync, which is very efficient and fast at recursively copying large numbers of deeply nested files.
When to use the copy module vs fetch module
The copy module is used when you want to put files onto the remote host, or you want to move files between places on the remote host. The fetch module is used when you want to move files from the remote host to the local host (i.e. fetch a file from the remote host).
When to use the copy module vs template module
The copy module is capable of putting dynamic (templated) text into a file like so:
- name: put templated text content into a file on the remote host
  copy:
    dest: $HOME/test_file
    content: "Hello, {{ ansible_user }}!"
This is fine for a small amount of text, but if you need to write a large amount of templated text (e.g. a configuration file with many templated lines) you should use the template module.
Where the copy module searches for files/directories on the local host
When copying from the local host, Ansible will search for files and directories in the ./files/ directory relative to where Ansible is being run. You can also supply an absolute path to the local file. Given the following file structure, the two plays have the same effect:
.
├── ansible.cfg
├── files
│   └── test_file
└── playbook.yml
# Recommended
- name: copy file from local host to remote host (relative path, ./files/)
  copy:
    src: test_file
    dest: $HOME/test_file
# NOT recommended
- name: copy file from local host to remote host (absolute path)
  copy:
    src: /path/to/ansible/files/test_file
    dest: $HOME/test_file
Given that the result is the same, I highly recommend using the relative file path since it will prevent errors if you ever move, copy or rename your Ansible directory.
Examples
How to copy a file from the local host to a remote host
You can use the src (local host) and dest (remote host) parameters to copy a file to a remote host. Note that the src searches the ./files directory relative to the playbook.
- name: copy file from local host to remote host
  copy:
    src: test_file
    dest: $HOME/test_file
How to copy a file from the local host to a remote host with owner, group and file permissions
The owner, group and mode parameters give you fine control over the ownership and file permissions of the file(s) created by the copy module. Remember that you will need become: true if you are setting the owner or group to values that don’t match the ansible_user.
The mode parameter accepts the file permissions setting in the following ways:
- An octal number: e.g. 0755,0644,0400
- Symbolic mode format: e.g. u=rw,g=r,o=r(whereuis the owner,gis the group, andois others). The permissions arerforread,wfor write andxfor execute.
With octal mode:
- name: copy file from local to remote with owner, group and file permissions (octal)
  copy:
    src: test_file
    dest: $HOME/test_file
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: 0644
With symbolic mode:
- name: copy file from local to remote with owner, group and file permissions (symbolic)
  copy:
    src: test_file
    dest: $HOME/test_file
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: u=rw,g=r,o=r
Setting a different owner with become: true:
- name: copy file from local to remote with root as the owner (become required)
  copy:
    src: test_file
    dest: "/home/{{ ansible_user }}/test_file"
    owner: root
    group: root
    mode: 0644
  become: true
How to copy a file between directories on a remote host with owner, group and file permissions
With octal mode:
- name: copy file between directories on remote with owner, group and file permissions (octal)
  copy:
    src: $HOME/test_file
    remote_src: true
    dest: $HOME/test_file2
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: 0644
With symbolic mode:
- name: copy file between directories on remote with owner, group and file permissions (symbolic)
  copy:
    src: $HOME/test_file
    remote_src: true
    dest: $HOME/test_file2
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: u=rw,g=r,o=r
Setting a different owner with become: true:
- name: copy file between directories on remote with root as the owner (become required)
  copy:
    src: $HOME/test_file
    remote_src: true
    dest: "/home/{{ ansible_user }}/test_file"
    owner: root
    group: root
    mode: 0644
  become: true
How to copy a directory from the local host to a remote host (recursive copy)
The copy module can be used to copy a directory from the local host to a remote host (called a recursive copy). To do a recursive copy, set the src parameter to a directory rather than a file.
There are two different behaviors:
- src: test_directory(without trailing slash): recursively copies the whole- test_directoryfrom- ./filesinto- dest
- src: test_directory/(with trailing slash): copies only the contents of- test_directoryrecursively into- dest
Let’s look at the results of recursive copy given the following file structure on the local host:
files
├── test_directory
│   ├── test_directory_2
│   │   └── test_file_2
│   └── test_file_1
└── test_file
Recursive copy without trailing slash
- name: copy directory recursively from local to remote (no trailing slash)
  copy:
    src: test_directory
    dest: $HOME
Resulting file structure on the remote:
.
└── test_directory
    ├── test_directory_2
    │   └── test_file_2
    └── test_file_1
Recursive copy with trailing slash
- name: copy directory recursively from local to remote (trailing slash)
  copy:
    src: test_directory/
    dest: $HOME
Resulting file structure on the remote:
.
├── test_directory_2
│   └── test_file_2
└── test_file_1
How to copy a directory between locations on the remote host (recursive copy)
This functionality is not currently supported by the copy module. If you want to recursively copy directories between locations on the remote host, please use the synchronize module.
How to write plain text into a file on the remote host with copy
You can write plain text to a file using the content parameter.
- name: write plain text into a file on the remote host
  copy:
    dest: $HOME/test_file
    content: "Hello, World!"
How to write templated text into a file on the remote host with copy
You can write templated text to a file using the content parameter. Although this is fine for simple one-line files, if you want to write multi-line templated text to a file, please use the template module.
- name: write templated text into a file on the remote host
  copy:
    dest: $HOME/test_file
    content: "Hello, {{ ansible_user }}!"
How to test that a file is valid before copying to the remote host
Use the validate parameter to check whether a file is valid on the remote host before copying the file into place. This parameter is highly recommended if you have some program that can confirm the whether a target file is valid.
%s in the validate string refers to the file to be checked.
How to check the validity of a sudoers file before copying
The /usr/sbin/visudo -cf command checks the validity of a sudoers file:
- name: update /etc/sudoers with validation
  copy:
    src: sudoers
    dest: /etc/sudoers
    owner: root
    group: root
    mode: 0400
    validate: /usr/sbin/visudo -cf %s
  become: true
How to check the validity of an nginx configuration file before copying
The /usr/bin/nginx -t -c command checks the validity of an nginx configuration file:
- name: update /etc/nginx/nginx.conf with validation
  copy:
    src: nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: 0644
    validate: /usr/bin/nginx -t -c %s
  become: true
How to backup a file if copy needs to overwrite it
By default, the copy module will overwrite a file if it already exists on the remote host. If you want to backup the file before it is overwritten, use the backup parameter:
- name: copy file from local host to remote host with backup
  copy:
    src: test_file
    dest: $HOME/test_file
    backup: true
With backup: true, copy will make a backup and append the timestamp before overwriting the file:
.
├── test_file
└── test_file.14792.2018-12-26@11:26:17~
How to copy multiple files with a loop
Use the loop keyword and {{ item }} to copy multiple files.
- name: copy multiple files from local host to remote host
  copy:
    src: test_file
    dest: "$HOME/test_file_{{ item }}"
  register: copy_output
  loop:
    - 1
    - 2
How to capture copy module output
Use the register keyword to capture the output of the copy module.
- name: copy file from local host to remote host
  copy:
    src: test_file
    dest: $HOME/test_file
  register: copy_output
- debug: var=copy_output
The debug task above will output the following:
ok: [123.123.123.123] => {
    "copy_output": {
        "changed": true,
        "checksum": "60fde9c2310b0d4cad4dab8d126b04387efba289",
        "dest": "/home/ubuntu/test_file",
        "diff": [],
        "failed": false,
        "gid": 1000,
        "group": "ubuntu",
        "md5sum": "bea8252ff4e80f41719ea13cdf007273",
        "mode": "0664",
        "owner": "ubuntu",
        "size": 14,
        "src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1545813585.53-237422897797330/source",
        "state": "file",
        "uid": 1000
    }
}
How to capture copy module output from a loop
When using the copy module in a loop, the output of each item in the loop will go into the results key of the registered variable.
- name: copy multiple files from local host to remote host
  copy:
    src: test_file
    dest: "$HOME/test_file_{{ item }}"
  register: copy_output
  loop:
    - 1
    - 2
- debug: var=copy_output
ok: [123.123.123.123] => {
    "copy_output": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                ...
                "changed": true,
                "checksum": "60fde9c2310b0d4cad4dab8d126b04387efba289",
                "dest": "/home/ubuntu/test_file_1",
                "diff": [],
                "failed": false,
                "gid": 1000,
                "group": "ubuntu",
                "invocation": {...},
                "item": "1",
                "md5sum": "bea8252ff4e80f41719ea13cdf007273",
                "mode": "0664",
                "owner": "ubuntu",
                "size": 14,
                "src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1545814066.74-176188973279099/source",
                "state": "file",
                "uid": 1000
            },
            {
                ...
                "changed": true,
                "checksum": "60fde9c2310b0d4cad4dab8d126b04387efba289",
                "dest": "/home/ubuntu/test_file_2",
                "diff": [],
                "failed": false,
                "gid": 1000,
                "group": "ubuntu",
                "invocation": {...},
                "item": "2",
                "md5sum": "bea8252ff4e80f41719ea13cdf007273",
                "mode": "0664",
                "owner": "ubuntu",
                "size": 14,
                "src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1545814068.75-122305147949956/source",
                "state": "file",
                "uid": 1000
            }
        ]
    }
}
Further reading
Ansible copy Module on Ansible Docs

