Fixing the Ansible 'could not find src' Error in the Copy Module

beginner🔧 Ansible2026-06-30| Ansible (all versions), Linux/macOS Control Node, Remote Target (Ubuntu/CentOS/Debian)

Error Message

fatal: [host]: FAILED! => {"changed": false, "msg": "could not find src=/path/to/local/file.conf"}
#ansible#devops#troubleshooting#automation#linux

The Problem Scenario

You are deploying a configuration file to a dozen web servers. You trigger your playbook, expecting a clean row of green 'changed' statuses. Instead, the process grinds to a halt with a blunt red error message:

fatal: [web_server]: FAILED! => {"changed": false, "msg": "could not find src=/etc/configs/nginx.conf"}

The confusion starts when you run ls -l /etc/configs/nginx.conf on your machine and see the file sitting right there. This error rarely means the file is missing from your disk. Usually, it means Ansible is looking in a different place than you intended due to how it resolves relative paths.

Why This Error Happens

The copy module transfers files from your control node (the laptop or CI/CD runner where you type the command) to the remote host. If the Ansible process cannot find the file locally, it cannot upload it. This failure typically stems from one of four issues:

  • Path Misalignment: You executed ansible-playbook from ~/projects/, but your source path is relative to ~/projects/ansible/playbooks/.
  • Role Hierarchy Breaches: You placed a file in a folder named config/ instead of the standard files/ directory within an Ansible role.
  • Variable Failures: A variable like {{ config_path }} is undefined or contains a typo, resulting in a null or broken path.
  • Permission Barriers: The local user executing Ansible lacks read permissions for the source file, making it "invisible" to the module.

Quick Fixes

1. Test with an Absolute Path

Use an absolute path as a diagnostic step. If the task succeeds with a full path, you have confirmed a relative pathing issue. For example, change your src to /home/dev/deploy/configs/app.conf. If this works, you know the file is accessible; only your mapping was off.

- name: Diagnostic copy using absolute path
  ansible.builtin.copy:
    src: /home/user/project/configs/app.conf
    dest: /etc/app/app.conf

2. Align with the Playbook Directory

By default, Ansible looks for files relative to the .yml file you are running. If your directory structure looks like the example below, your src should simply point to the folder sitting next to your playbook.

# Directory structure:
# ├── site.yml
# └── files/
#     └── app.conf

- name: Copy app configuration
  ansible.builtin.copy:
    src: files/app.conf
    dest: /etc/app/app.conf

Permanent Fixes and Best Practices

Leverage the Role Directory Structure

Roles provide a built-in search logic that simplifies your tasks. When a task is inside roles/webserver/tasks/main.yml, Ansible automatically scans roles/webserver/files/ for any source files. You can omit the path entirely.

Standard Role Structure:

roles/
  common/
    tasks/main.yml
    files/ntp.conf

The Task in main.yml:

- name: Deploy NTP config
  ansible.builtin.copy:
    src: ntp.conf  # Ansible finds this automatically in ../files/
    dest: /etc/ntp.conf

Anchor Paths with playbook_dir

Complex projects often involve running playbooks from different subfolders. To keep your paths portable, use the playbook_dir magic variable. This ensures the path is always calculated from the root of your playbook folder, regardless of where you initiate the command.

- name: Copy script from project root
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/scripts/setup.sh"
    dest: /usr/local/bin/setup.sh
    mode: '0755'

Debug the Local Controller

If the error persists, force Ansible to report what it sees on your local machine. Use the stat module and delegate_to: localhost to verify the file's existence before the copy attempt. This is the fastest way to catch permission issues or empty variables.

- name: Verify local file existence
  ansible.builtin.stat:
    path: "{{ playbook_dir }}/files/my-config.conf"
  register: local_file
  delegate_to: localhost

- name: Fail if file is missing
  ansible.builtin.fail:
    msg: "The file was not found on the control node!"
  when: not local_file.stat.exists

Verification Steps

After applying a fix, verify the solution with these three steps:

  • Increase Verbosity: Run ansible-playbook -vv. The output will reveal the exact absolute path Ansible is trying to open on your local disk.
  • Perform a Dry Run: Execute with --check. If the 'could not find src' error is gone, Ansible has successfully mapped the local file and is ready to push it.
  • Confirm Permissions: Ensure the local file has at least 644 permissions (-rw-r--r--) so the Ansible user can read it.

Using a standard role structure or anchoring paths with {{ playbook_dir }} eliminates 90% of these errors. These habits make your automation predictable and portable across different team environments.

Related Error Notes