Ansible Troubleshooting Guide: Fix Common Playbook, SSH, sudo and Inventory Errors

Troubleshoot Ansible playbooks from inventory and SSH through sudo, YAML syntax, variables, collections, Vault, and templates—with commands and error tables for common failures.

Published

Updated

Read time 17 min read

Reviewed byDeepak Prasad

Ansible troubleshooting guide for inventory, SSH, sudo and playbook errors on Rocky Linux 10

Ansible errors are easier to fix when you troubleshoot them in layers. Do not start by editing the failed task immediately. First confirm that Ansible can parse the inventory, target the expected hosts, connect over SSH, become the required user, parse the playbook, load variables and collections, decrypt Vault data, render templates, and then run the task logic.

When a playbook fails, check these layers in order:

  1. Can Ansible parse the inventory?
  2. Does the playbook target the expected hosts?
  3. Can Ansible connect over SSH?
  4. Can the remote user use sudo or become when required?
  5. Is the playbook valid YAML?
  6. Are variables, facts, collections, Vault files, and templates loading correctly?
  7. Is the failed task itself written correctly?

Tested on: Rocky Linux 10.2 (Red Quartz); kernel 6.12.0-211.16.1.el10_2.0.1.x86_64; ansible-core 2.16.16.

NOTE
This chapter is part of the GoLinuxCloud Ansible tutorial (RHCE EX294). Follow along from ~/ansible-project, inventory group lab, and playbooks in playbooks/. Use your own host names and paths if yours differ.
IMPORTANT
This is a troubleshooting hub—symptom tables and first commands for each layer. It does not replace dedicated guides for inventory design, YAML syntax, Vault, templates, collections, deep debug techniques, or focused collection and variable errors.

Need Use this page for Dedicated guide
Playbook failed and you do not know why Identify the failing layer This troubleshooting hub

How to Troubleshoot Ansible Errors

Work from the outside in: connectivity and parsing problems look like task failures but are not fixed by changing module arguments.

Play recap status Meaning
ok Task ran; desired state already met
changed Task ran and modified the host
skipped when: or other condition prevented execution
unreachable Ansible could not connect or run Python on the host
failed Task ran but reported an error

When output is not obvious, rerun with more verbosity (-v through -vvvv) or target one host with -l rocky2 so logs stay readable. For playbook CLI flags and check mode, see how to run Ansible playbooks. Quick module probes use ad hoc commands.


Quick Ansible Troubleshooting Checklist

Run these commands before you edit playbook tasks. Each row is a quick yes/no check for one layer—inventory, connectivity, syntax, or collections. If a command fails or looks wrong, skip to the matching section below (Inventory, SSH, YAML, and so on). This table tells you where to look; the sections under Common Ansible Errors and Quick Fixes show what to change.

If your project ansible.cfg already sets inventory = inventory/hosts, you can omit -i inventory/hosts on commands below.

Check Command What it confirms
Ansible runtime ansible --version Config file, Python version, module path, collection paths
Inventory parses ansible-inventory -i inventory/hosts --list Inventory syntax and host vars load
Groups are visible ansible-inventory -i inventory/hosts --graph Groups and hosts are wired correctly
Play targets hosts ansible-playbook -i inventory/hosts playbooks/ping-lab.yml --list-hosts hosts: pattern matches expected hosts
SSH works ansible -i inventory/hosts lab -m ansible.builtin.ping Ansible can connect and run Python
SSH debug ansible -i inventory/hosts rocky2 -m ansible.builtin.ping -vvvv Shows SSH user, key, port, and remote command
Syntax is valid ansible-playbook -i inventory/hosts playbooks/ping-lab.yml --syntax-check YAML and playbook structure parse
Tasks are visible ansible-playbook -i inventory/hosts playbooks/ping-lab.yml --list-tasks Task order and imports are visible
Dry run ansible-playbook -i inventory/hosts playbooks/file-demo.yml --check --diff Shows planned changes where supported
Config check ansible-config dump --only-changed Active ansible.cfg overrides
Collections ansible-galaxy collection list Installed collections and versions

ansible --version is the first command when a playbook works on your laptop but fails in CI—it shows which config file, Python, and collection paths that shell is using:

bash
ansible --version

Sample output:

output
ansible [core 2.16.16]
  config file = /home/ansible/ansible-project/ansible.cfg
  configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.12/site-packages/ansible
  ansible collection location = /home/ansible/.ansible/collections:/usr/share/ansible/collections

If config file points at the wrong path, Ansible may be ignoring your project ansible.cfg.

To confirm groups and hosts before running a play, list the inventory tree from the project directory:

bash
cd ~/ansible-project
bash
ansible-inventory -i inventory/hosts --graph

Sample output:

output
@all:
  |--@ungrouped:
  |--@lab:
  |  |--rocky2

The lab group contains rocky2—if your play uses hosts: lab, that hostname should also appear when you run --list-hosts.


Build a Minimal Reproducer

Before you edit a large playbook, shrink the failure to the smallest command sequence that still fails. Work through these steps in order—stop when one step breaks and fix that layer before returning to the full playbook.

Step Command What it isolates
1. Inventory + SSH ansible -i inventory/hosts rocky2 -m ansible.builtin.ping Host pattern, connectivity, Python on the target
2. Module FQCN ansible-doc -t module ansible.builtin.copy Correct module name and documented options
3. Playbook parse ansible-playbook -i inventory/hosts playbooks/site.yml --syntax-check YAML structure and task keys
4. Dry run ansible-playbook -i inventory/hosts playbooks/site.yml --check Planned changes without applying them (where modules support check mode)
5. SSH detail ansible -i inventory/hosts rocky2 -m ansible.builtin.ping -vvv SSH user, key, port, and remote command Ansible actually uses

Step 1 confirms Ansible can reach the host at all. Step 2 catches typos and wrong collection namespaces before you chase playbook logic. Steps 3–4 separate parse errors from runtime module failures. Step 5 is the first verbosity level that usually shows enough SSH detail without drowning in connection plugin noise—use -vvvv only when -vvv still hides the handshake problem.

When the full playbook fails but ping succeeds, copy the failing task into a one-task play (or use --start-at-task) and rerun the same five steps against that smaller file.


Common Ansible Errors and Quick Fixes

Error message Usually means Start here
provided hosts list is empty Inventory not loaded or empty Check -i inventory/hosts and run ansible-inventory --list
Could not match supplied host pattern hosts: does not match inventory Run ansible-inventory --graph and --list-hosts
UNREACHABLE! SSH, DNS, firewall, user, key, or port issue Run ansible host -m ansible.builtin.ping -vvvv
Permission denied (publickey,password) Wrong SSH user, password, or key Check ansible_user, key path, and manual SSH
Missing sudo password Become password required Run with -K or configure sudoers carefully
sudo: a password is required become: true but no password supplied Add --ask-become-pass or use NOPASSWD carefully
mapping values are not allowed here YAML indentation, colon, or quoting issue Run --syntax-check
couldn't resolve module/action Missing collection, typo, or wrong module name Use FQCN and install the collection
Attempting to decrypt but no vault secrets found Vault password not supplied Use --ask-vault-pass or --vault-id
Decryption failed Wrong Vault password or Vault ID Check vault ID and password source
template error while templating string Jinja2 syntax, filter, or variable issue Check template and variable values
Task changes every run Non-idempotent task or volatile template content See idempotency

Inventory and Host Pattern Errors

These errors usually appear when Ansible never loads your inventory file, the hosts: line does not match any group or host name, or --limit narrows the run to zero targets. A typo in a group name (labs instead of lab) or running from the wrong directory without -i are frequent causes.

Symptom Common cause First check
provided hosts list is empty Wrong -i path, empty inventory, or command run outside the project ansible-inventory -i inventory/hosts --list
Could not match supplied host pattern Typo in hosts: or group name ansible-inventory --graph
Play runs on zero hosts --limit too narrow or pattern mismatch ansible-playbook -i inventory/hosts site.yml --list-hosts
Wrong host contacted ansible_host override or DNS issue Inspect inventory host vars

Confirm the play targets the hosts you expect before changing tasks. From the project directory, list which hosts match the play's hosts: pattern:

bash
cd ~/ansible-project
bash
ansible-playbook -i inventory/hosts playbooks/ping-lab.yml --list-hosts

Sample output:

output
play #1 (lab): Ping lab hosts	TAGS: []
    pattern: ['lab']
    hosts (1):
      rocky2

The hosts (1): line is what you want—a non-zero count means Ansible will run tasks on those machines.

When no host matches, Ansible stops before any task runs:

output
ERROR! Specified inventory, host pattern and/or --limit leaves us with no hosts to target.

Fix the inventory path, correct the hosts: or --limit value, then rerun --list-hosts until the expected hostnames appear.

Inventory variables such as ansible_host, ansible_user, ansible_port, and ansible_connection change how Ansible connects—details live in inventory files and group_vars patterns.


SSH Connection Errors

UNREACHABLE! and Permission denied appear when Ansible cannot open an SSH session to the managed host—wrong IP or DNS in ansible_host, firewall blocking port 22, sshd down, incorrect ansible_user, or a key/password mismatch. When keys are not an option, password-based SSH with --ask-pass is covered in use Ansible with password. These failures happen before module logic runs, so fixing the task YAML will not help until connectivity works.

Symptom Common cause First check
UNREACHABLE! Network, SSH service, user, key, or port Manual SSH, then ansible … -m ping -vvvv
Permission denied (publickey,password) Wrong user or key ansible_user, ansible_ssh_private_key_file
Connection timeout Firewall, wrong IP, SSH down ansible_host, test port 22 manually
Wrong user on host Default remote_user vs inventory ansible-config dump --only-changed
Python interpreter error Minimal image, wrong interpreter, or missing Python ansible_python_interpreter; ansible -m setup -vvvv

The ping module is not ICMP ping—it opens SSH and runs a small Python module on the managed host. Manual SSH can succeed while ansible.builtin.ping still fails when Python is missing or interpreter discovery picks the wrong binary. Set ansible_python_interpreter in inventory when needed, or install Python on minimal images before running modules.

Verify SSH outside Ansible with the same user and address from inventory:

bash
ssh [email protected] echo ok

Sample output:

output
ok

Then confirm Ansible can run a module on the host:

bash
ansible -i inventory/hosts lab -m ansible.builtin.ping

Sample output:

output
rocky2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

When the address in inventory is wrong, the host is offline, or routing fails, you see UNREACHABLE! similar to:

output
rocky2 | UNREACHABLE! => {
    "msg": "Data could not be sent to remote host \"192.168.56.199\". Make sure this host can be reached over ssh: ssh: connect to host 192.168.56.199 port 22: No route to host\r\n",
    "unreachable": true
}

Correct ansible_host, confirm the host is up, then rerun ping. For stubborn cases, add -vvvv and look for the SSH user and target IP Ansible actually used:

output
<192.168.56.109> ESTABLISH SSH CONNECTION FOR USER: ansible
<192.168.56.109> SSH: EXEC ssh ... -o 'User="ansible"' ... 192.168.56.109 ...

If manual SSH works but Ansible fails, compare ansible_user, private key settings, and ansible.cfg remote_user against what succeeded on the command line.


sudo and become Errors

Package installs, service management, and writes under /etc fail with permission errors when become: true is missing or when the remote user must type a sudo password Ansible was not given. This often shows up after connectivity works—ping succeeds but dnf or service tasks fail.

Symptom Common cause Fix
Permission denied on /etc or package task Missing become: true Add become: true to play or task
Missing sudo password sudo requires an interactive password ansible-playbook site.yml -K
sudo: a password is required Become enabled but no password supplied --ask-become-pass or passwordless sudo for automation user
user is not allowed to execute Remote user not in sudoers Fix sudoers or use a user with privilege
Works as root manually but fails in Ansible Ansible connects as a different user Check ansible_user and become

For package, service, and /etc changes, set become: true at the play level when most tasks need privilege:

yaml
- name: Configure managed host
  hosts: lab
  become: true
  tasks:
    - name: Install package
      ansible.builtin.dnf:
        name: httpd
        state: present

ansible.cfg may set become = True globally—if privileged tasks still fail, the ansible user likely needs NOPASSWD sudo or you must pass -K when sudo requires a password.


YAML and Playbook Syntax Errors

Syntax errors surface when you run ansible-playbook or --syntax-check—often after a rushed edit: module arguments placed on the same line as the module name, tabs mixed with spaces, or a missing - before a task. Ansible stops at parse time and points at the offending line.

Error When it usually appears
mapping values are not allowed here Module params on the same line as module_name:
did not find expected key Bad indentation under tasks:
found character '\t' Tab characters in the YAML file
Task parsed as one long line Missing - list marker before a task
Unexpected boolean Unquoted yes/no/on/off treated as YAML booleans

When YAML and structure are valid, syntax check prints only the playbook path:

bash
ansible-playbook -i inventory/hosts playbooks/ping-lab.yml --syntax-check

Sample output:

output
playbook: playbooks/ping-lab.yml

When structure is wrong, Ansible names the line—here the mistake is inline msg: after ansible.builtin.debug: instead of an indented msg: key:

output
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
Syntax Error while loading YAML.
  mapping values are not allowed in this context

The offending line appears to be:

    - name: broken
      ansible.builtin.debug: msg: hello
                                ^ here

Fix indentation and move module arguments under the module name, then rerun --syntax-check until it returns only the playbook path. Full YAML rules live in YAML syntax.


Variable, Collection and Module Errors

This section only covers first checks. If the error mentions couldn't resolve module/action, missing collection paths, FQCN, or undefined variables, use the dedicated collection and variable error guide for the full fix.

These messages appear after Ansible connects and parses the playbook: a typo in a module name, a collection missing from the control node, or a variable referenced before it is defined in inventory, play vars, or role defaults.

Error Typical scenario Fix
couldn't resolve module/action Typo or collection not installed on control node FQCN + ansible-galaxy collection install
Collection installed but not found Collection path in CI differs from laptop Check COLLECTIONS_PATHS / collection paths from ansible --version
Works locally but fails in CI Pipeline skipped requirements.yml Install collections before ansible-playbook
'myvar' is undefined Var used in task before defined in scope Define in inventory, play, or role defaults
Template variable missing Jinja references unset fact or var Debug value before template task

A mistyped module name fails immediately:

output
rocky2 | FAILED! => {
    "msg": "The module nonexistent.module was not found in configured module paths"
}

Use the FQCN (ansible.builtin.copy, community.general.sefcontext) and confirm the collection is listed in ansible-galaxy collection list.

Referencing a variable that was never set in scope—for example in a command or template line—produces:

output
fatal: [rocky2]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'my_missing_var' is undefined."}

Trace where the variable should come from (variables, group_vars, role defaults/) before changing the task. Deeper FQCN, path, and Jinja2 coverage is in collection and variable errors and collections overview.


Ansible Vault Errors

Vault errors appear when playbooks or group_vars contain encrypted content but no password or vault ID was supplied—common on first run, in CI without a secret, or after copying encrypted files without updating the password source.

Error Typical scenario Fix
Attempting to decrypt but no vault secrets found First run, CI job, or encrypted group_vars without a password --ask-vault-pass, --vault-password-file, or --vault-id
Decryption failed Wrong password or vault ID label Match vault ID in file header; re-enter correct password
Encrypted vars not loaded Encrypted file outside Ansible search path Place in group_vars, host_vars, or vars_files
CI cannot decrypt Pipeline has no vault secret configured Inject password file or env from CI secret store

Example error:

Sample output:

output
ERROR! Attempting to decrypt but no vault secrets found

Start by rerunning the playbook with --ask-vault-pass or the correct --vault-id.

Vault workflow—encrypting strings, editing encrypted files, and group_vars layout—is in Ansible Vault. This hub only maps error text to the missing password or file.


Template and Jinja2 Errors

Template and Jinja2 failures happen when Ansible renders a template file or evaluates Jinja2 expressions inside task arguments—syntax mistakes, undefined variables in {{ }}, or filters from a collection that is not installed. A template that embeds timestamps can also report changed on every run even when configuration is logically the same.

Symptom Typical scenario Fix
template error while templating string Bad {% %} / {{ }} or typo in expression Fix Jinja2 syntax in template
Undefined variable in template Fact or var not set for that host Define var or use default() only when optional
Missing filter Filter lives in uninstalled collection Install collection; use FQCN
Template changed every run Timestamp or random value in rendered file Remove volatile content from template

Before a template task fails on an undefined variable, print the values the template depends on:

yaml
- name: Show template inputs
  ansible.builtin.debug:
    msg:
      app_port: "{{ app_port | default('undefined') }}"
      app_env: "{{ app_env | default('undefined') }}"

Full debug, assert, and verbosity workflows live in debug Ansible playbooks.


File, Permission and Path Errors

These show up when a task runs but cannot read a source file, write a destination, or the service cannot use a path after copy—often missing become, wrong local vs remote path, or SELinux context blocking the daemon.

Symptom Typical scenario Fix
dest permission denied Writing to /etc without root become: true
src file not found Path on control node does not exist Fix path relative to playbook or role
remote_src confusion Source file for copy exists on managed host, not control node Use remote_src: true with copy, or use fetch when pulling files back
Service cannot read file SELinux type blocks daemon Check ls -Z; see SELinux contexts
Wrong module for the job copy vs template vs file Copy and fetch modules

Use copy when the file starts on the control node and must go to the managed host. Use fetch when the file starts on the managed host and must come back to the control node.

For copy and template, src is normally read from the control node, while dest is written on the managed host. When the source file for copy already exists on the managed host, set remote_src: true.

When a symlink or copy references a path that does not exist yet, Ansible reports src file does not exist—fix task order so directories and files are created before dependent tasks run.


Debugging Failed Tasks

When connectivity, YAML, and variables look fine, inspect runtime state before rewriting the task:

Tool Use for
ansible.builtin.debug Print vars and registered results
register Capture module output for later tasks
ansible.builtin.assert Fail early when a condition is wrong
--check Dry-run supported modules
--diff Show file changes with check mode
--start-at-task Resume after a known-good task
--step Confirm each task interactively
-vvv Module payload and connection detail

This section helps you choose the debugging tool. The next guide shows full examples with debug, assert, registered variables, facts, check mode, diff mode, and verbosity in debug Ansible playbooks.


Useful Commands for Ansible Troubleshooting

bash
ansible --version
ansible-config dump --only-changed
ansible-inventory -i inventory/hosts --list
ansible-inventory -i inventory/hosts --graph
ansible-playbook -i inventory/hosts playbooks/ping-lab.yml --list-hosts
ansible-playbook -i inventory/hosts playbooks/ping-lab.yml --syntax-check
ansible-playbook -i inventory/hosts playbooks/ping-lab.yml --list-tasks
ansible-playbook -i inventory/hosts playbooks/file-demo.yml --check --diff
ansible -i inventory/hosts lab -m ansible.builtin.ping
ansible -i inventory/hosts rocky2 -m ansible.builtin.ping -vvvv
ansible-galaxy collection list
ansible-doc ansible.builtin.copy
ansible-vault view group_vars/all/vault.yml

Run these from the project directory that contains ansible.cfg so inventory and collection paths match your playbooks.


Best Practices to Avoid Ansible Errors

Practice Why
Start with inventory and connectivity checks Avoids debugging the wrong layer
Use --list-hosts before risky plays Confirms the play targets the right hosts
Run --syntax-check after edits Catches YAML and structure errors early
Use --check --diff for file changes Shows expected diffs where modules support it
Use FQCN for modules Reduces collection ambiguity
Commit role and collection requirements Makes CI and teammates reproducible
Keep Vault secrets out of plain vars Prevents leakage in git
Use debug and register during investigation Shows actual runtime values
Remove temporary debug tasks after fixing Keeps playbooks clean
Avoid broad ignore_errors: true Hides real failures

Summary

Troubleshoot Ansible from the outside in: inventory and host patterns, SSH, sudo/become, YAML syntax, then variables, collections, Vault, templates, and finally task logic. Use the checklist commands and error tables to narrow the layer fast, then follow the dedicated guide for that topic. For runtime inspection, continue to debug Ansible playbooks; for couldn't resolve module/action and undefined variables in depth, see collection and variable errors.


References

  • Ansible troubleshooting (community tips) — official tips
  • Inventory — host patterns and variables
  • Become (privilege escalation) — sudo and become flags
  • Ansible inventory files — inventory design
  • YAML syntax — indentation and quoting
  • Ansible variables — variable basics
  • Collections and RHEL System Roles — FQCN and install paths
  • Ansible Vault — encrypt and decrypt workflow
  • Template module — Jinja2 in playbooks

Frequently Asked Questions

1. How do I troubleshoot an Ansible playbook error?

Start with inventory and host targeting, then check SSH connectivity, sudo/become access, YAML syntax, variables, collections, Vault data, templates, and finally the failed task logic.

2. How do I debug an Ansible SSH connection error?

Test manual SSH first, then run ansible with the ping module and -vvvv to see the SSH user, key, port, and command Ansible is using.

3. How do I check which hosts an Ansible playbook will run on?

Run ansible-playbook -i inventory playbook.yml --list-hosts to confirm the hosts matched by the play before executing tasks.

4. How do I fix sudo password errors in Ansible?

Use become: true for privileged tasks and run with -K or --ask-become-pass when the remote user requires a sudo password.

5. How do I find missing collection or module errors?

Use the module FQCN, check installed collections with ansible-galaxy collection list, and install required collections from requirements.yml.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …