Ansible copy Module Tutorial + Examples


Percy Grunwald's Profile Picture

Written by Percy Grunwald

— Last Updated August 2, 2020

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_src parameter)
- 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 content parameter)
- 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.

▶️ Watch on YouTube

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 (where u is the owner, g is the group, and o is others). The permissions are r for read, w for write and x for 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:

  1. src: test_directory (without trailing slash): recursively copies the whole test_directory from ./files into dest
  2. src: test_directory/ (with trailing slash): copies only the contents of test_directory recursively 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

Comment & Share