Fix Ansible "Could not find or access 'templates/nginx.conf.j2'" Error

beginner๐Ÿ”ง Ansible2026-03-18| Ansible 2.9+, Linux/macOS Controller, any managed host

Error Message

fatal: [host]: FAILED! => {"changed": false, "msg": "Could not find or access 'templates/nginx.conf.j2' on the Ansible Controller."}
#template#jinja2#j2#roles#templatefile

The Error

You kick off an Ansible playbook and it dies immediately with:

fatal: [host]: FAILED! => {"changed": false, "msg": "Could not find or access 'templates/nginx.conf.j2' on the Ansible Controller."}

Nine times out of ten, the task using ansible.builtin.template (or the shorthand template) is the culprit. Notice the wording: Ansible searched on the controller โ€” the machine where you ran ansible-playbook โ€” not the remote host. The file simply wasn't where Ansible expected it.

Why This Happens

Ansible already knows to look inside a templates/ subdirectory relative to your playbook or role. That's built into the lookup logic. So src should contain just the filename โ€” nginx.conf.j2, not templates/nginx.conf.j2.

Two things cause this error almost every time:

  • You wrote src: templates/nginx.conf.j2, so Ansible looks for templates/templates/nginx.conf.j2. That path doesn't exist.
  • The .j2 file is in the wrong directory โ€” not where Ansible actually searches.

Step-by-Step Fix

Step 1 โ€” Check How You're Calling the Template Module

Look at the src field in your task:

# WRONG โ€” don't include the "templates/" prefix
- name: Deploy nginx config
  ansible.builtin.template:
    src: templates/nginx.conf.j2
    dest: /etc/nginx/nginx.conf

# CORRECT โ€” just the filename
- name: Deploy nginx config
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf

Ansible already searches templates/ next to your playbook or inside your role. Adding the prefix doubles the path to something like templates/templates/nginx.conf.j2 โ€” which never exists.

Step 2 โ€” Confirm the File Exists in the Right Place

Scenario A: Playbook-level template (no roles)

The template must live in a templates/ directory right next to your playbook:

site.yml
templates/
  nginx.conf.j2    โ† must be here

Quick check:

ls -la templates/nginx.conf.j2

Scenario B: Inside a role

Tasks inside a role look in that role's own templates/ folder โ€” not the project root:

roles/
  nginx/
    tasks/
      main.yml
    templates/
      nginx.conf.j2    โ† must be here

Quick check:

ls -la roles/nginx/templates/nginx.conf.j2

The classic mistake: you drop the template in the project root's templates/ but run the task from inside a role. Ansible searches the role's directory first and never finds it there.

Step 3 โ€” Fix the Directory Structure

Missing directory or wrong location? Create it and move the file:

# For a role named "nginx"
mkdir -p roles/nginx/templates
mv nginx.conf.j2 roles/nginx/templates/

# For a playbook-level template
mkdir -p templates
mv nginx.conf.j2 templates/

Step 4 โ€” Check for Typos and Case Sensitivity

Linux filesystems are case-sensitive. Nginx.conf.j2 and nginx.conf.j2 are two completely different files. Confirm the exact filename on disk:

ls templates/
# Should show: nginx.conf.j2 (not Nginx.conf.j2)

Step 5 โ€” Using an Absolute Path (Last Resort)

Got a template stored in a non-standard location? Pass the full path directly:

- name: Deploy nginx config
  ansible.builtin.template:
    src: /opt/configs/nginx.conf.j2
    dest: /etc/nginx/nginx.conf

It works, but it ties the playbook to a specific machine. Avoid this in anything you plan to share or reuse.

Verify the Fix

Crank up verbosity to watch exactly which path Ansible resolves:

ansible-playbook site.yml -vvv 2>&1 | grep -i "template\|j2"

When it works, you'll see something like:

TASK [Deploy nginx config] ****
ok: [host] => changed=true

Want to test without touching any files? Add --check:

ansible-playbook site.yml --check

Quick Lookup: Where Ansible Searches for Templates

# Playbook-level (site.yml at project root)
./templates/
./

# Role-level (task inside roles/nginx/tasks/main.yml)
roles/nginx/templates/
roles/nginx/
./templates/      โ† also checked, but role templates take priority
./

Ansible works down this list and uses the first match it finds. The templates/ directory is already part of the search โ€” that's exactly why prefixing src with it causes the double-path lookup failure.

Tips

  • Never prefix src with templates/ โ€” this is the #1 cause of this error, and it's easy to miss on a quick glance.
  • Moving tasks from a flat playbook into a role? Move the templates into the role's templates/ directory at the same time, or they'll fall outside Ansible's search path.
  • Subdirectories inside templates/ work fine in src: src: nginx/nginx.conf.j2 resolves to templates/nginx/nginx.conf.j2.
  • Run ansible-playbook --list-tasks to confirm which role owns a task before you start hunting for the template path.

Related Error Notes