Troubleshooting Ansible¶
Overview¶
Most Ansible failures are not mysterious — they cluster into a short list: invalid YAML, SSH or privilege escalation problems, wrong inventory host, undefined variables, or a module returning failed. A fixed triage order (--syntax-check → inventory ping → -vvv task output) saves hours during change windows.
This is Tutorial 17 in Module 17: Troubleshooting of the REBASH Academy Ansible for Cloud & DevOps Engineers series — written for Cloud, DevOps, Platform, and SRE engineers. Reference: Ansible debugging guide.
Prerequisites¶
- Production Ansible Practices
- Basic SSH troubleshooting on Linux
Learning Objectives¶
By the end of this tutorial, you will be able to:
- Fix YAML and Jinja errors caught by
--syntax-check - Diagnose inventory mismatches and unreachable hosts
- Use
-vvvverbosity to inspect module arguments and responses - Repair a broken playbook and capture before/after evidence
- Document common failure modes in a triage table
Architecture¶
Failures originate in playbook syntax, inventory resolution, transport (SSH), variable precedence, or module execution on the target.
Theory¶
What it is¶
Ansible troubleshooting follows layers:
| Layer | Tools | Typical errors |
|---|---|---|
| Syntax | ansible-playbook --syntax-check, ansible-lint | Indentation, undefined Jinja |
| Inventory | ansible-inventory --graph, --list | Missing host, wrong group |
| Connectivity | ansible -m ping, ssh -vvv | Timeout, key, sudo |
| Variables | ansible-playbook -e debug_var, -vvv | Undefined, wrong precedence |
| Modules | -vvv, register + debug | Permission, idempotency, bad args |
Why it matters¶
On-call engineers need a playbook-independent checklist. The same steps apply whether the failure is nginx config or a Kubernetes bootstrap role.
How it works¶
Standard triage:
- Syntax-check locally — no SSH required.
- Inventory graph — confirm host is in expected group.
- Ping —
ansible target -m ping -i inventories/prod. - Increase verbosity —
-v…-vvvvon failing play. - Isolate —
--tagsor--start-at-taskto rerun one task. - Validate vars —
hostvars,group_vars, Vault decrypt.
Key concepts and comparisons¶
| Symptom | Often actually |
|---|---|
UNREACHABLE! | SSH/firewall/wrong ansible_host |
undefined variable | Typo or missing group_vars |
Permission denied | become / sudoers / remote user |
Module not found | Collection not installed |
| Silent wrong behaviour | Cached facts or wrong inventory -i |
Common pitfalls¶
- Editing production before syntax-check — Fix: always run
--syntax-checkfirst. - Assuming
hosts: allis safe — Fix: use limits (--limit) in prod. - Ignoring
changed=falsebut task failed — Fix: readfailed_when/ignore_errorslogic.
Hands-on Lab¶
Objective¶
Create a deliberately broken playbook that fails --syntax-check, fix it, demonstrate an inventory host miss, and capture before/after evidence logs.
Prerequisites¶
- Ansible 2.18+
- No remote hosts required (localhost inventory)
Lab environment¶
Workspace: ~/rebash-ansible/module-17
mkdir -p ~/rebash-ansible/module-17/{inventories,playbooks} && cd ~/rebash-ansible/module-17
Real-world scenario¶
A teammate pushed a hotfix playbook during an incident. CI was bypassed. You must reproduce the syntax failure, fix it, and prove an inventory typo would have caused a zero-host play before anyone ran against production.
Step-by-step tasks¶
Task 1 – Create broken playbook and capture syntax failure¶
Create inventories/lab/hosts.yml:
Create playbooks/broken-site.yml with an intentional YAML error (bad indent on tasks):
---
- name: Broken site play
hosts: localhost
gather_facts: false
tasks:
- name: Broken indent task
ansible.builtin.debug:
msg: "this indent is invalid"
Run syntax-check and save failure evidence:
cd ~/rebash-ansible/module-17
ansible-playbook --syntax-check -i inventories/lab playbooks/broken-site.yml \
> syntax-before.txt 2>&1 || true
grep -Ei 'error|yaml|while scanning' syntax-before.txt
Expected output
Non-zero exit; log contains YAML/syntax error text.
Task 2 – Fix playbook and capture passing syntax-check¶
Create playbooks/fixed-site.yml:
---
- name: Fixed site play
hosts: localhost
gather_facts: false
tasks:
- name: Valid debug task
ansible.builtin.debug:
msg: "syntax OK after fix"
Run syntax-check on fixed playbook:
cd ~/rebash-ansible/module-17
ansible-playbook --syntax-check -i inventories/lab playbooks/fixed-site.yml \
| tee syntax-after.txt
grep -q 'playbook: playbooks/fixed-site.yml' syntax-after.txt
! grep -Ei 'error|fatal' syntax-after.txt
Expected output
Syntax check passes; no error/fatal lines in output.
Task 3 – Demonstrate inventory host miss¶
Create playbooks/needs-app-group.yml:
---
- name: Play targeting missing group
hosts: app
gather_facts: false
tasks:
- name: Should not run if inventory miss
ansible.builtin.debug:
msg: "app tier task"
Create inventories/missing-app/hosts.yml (no app group):
Run with verbosity and capture zero-host behaviour:
cd ~/rebash-ansible/module-17
ansible-playbook -i inventories/missing-app playbooks/needs-app-group.yml -vv \
| tee inventory-miss.txt
grep -Ei 'skipping.*no hosts matched|empty|0 hosts' inventory-miss.txt
Expected output
Play skips or reports no matching hosts for app.
Fix inventory — create inventories/with-app/hosts.yml:
Re-run:
cd ~/rebash-ansible/module-17
ansible-playbook -i inventories/with-app playbooks/needs-app-group.yml \
| tee inventory-hit.txt
grep -q 'app tier task' inventory-hit.txt
grep -q 'PLAY RECAP' inventory-hit.txt
Expected output
Debug message appears; play completes.
Task 4 – Module failure demo with -vvv evidence¶
Create playbooks/module-fail.yml:
---
- name: Module failure demo
hosts: localhost
gather_facts: false
tasks:
- name: Run missing command
ansible.builtin.command:
cmd: /usr/bin/this-binary-does-not-exist-rebash
register: cmd_out
ignore_errors: true
- name: Show failure details
ansible.builtin.debug:
var: cmd_out
Run with high verbosity:
cd ~/rebash-ansible/module-17
ansible-playbook -i inventories/lab playbooks/module-fail.yml -vvv \
| tee module-fail-vvv.txt
grep -q 'rc' module-fail-vvv.txt
grep -q 'this-binary-does-not-exist-rebash' module-fail-vvv.txt
Expected output
Verbose log shows command and non-zero return code in registered var.
Task 5 – Package before/after evidence tarball¶
cd ~/rebash-ansible/module-17
tar -czf module-17-evidence.tgz \
inventories/ playbooks/ \
syntax-before.txt syntax-after.txt \
inventory-miss.txt inventory-hit.txt module-fail-vvv.txt
ls -lh module-17-evidence.tgz | tee tarball.txt
test -s module-17-evidence.tgz
Expected output
Tarball contains broken vs fixed syntax logs and inventory miss/hit proof.
Validation steps¶
- Broken playbook fails
--syntax-checkwith captured log - Fixed playbook passes syntax-check
- Inventory without
appgroup produces skip/no-host evidence - Corrected inventory runs the task
-
-vvvmodule failure log captured
Common errors and fixes¶
| Error | Cause | Fix |
|---|---|---|
| YAML scanner error | Indentation under tasks | Align list items with two spaces consistently |
provided hosts list is empty | Wrong group or limit | ansible-inventory --graph; fix group name |
sudo: a password is required | Missing become password | --ask-become-pass or passwordless sudo |
couldn't resolve module | Collection not installed | ansible-galaxy collection install … |
Too much -vvvv noise | Log volume | Start -v, increase until module args visible |
Challenge exercise¶
Add a block/rescue wrapper around the failing command task that writes a one-line incident note to /tmp/ansible-rescue.log on localhost when rescue triggers.
Learning outcomes¶
- Repeatable syntax and inventory triage
- Verbosity skills for module-level failures
- Before/after artefacts suitable for post-incident review
Cleanup¶
Validation¶
- All lab steps run under
~/rebash-ansible/module-17/ - Before and after syntax logs differ as expected
- Inventory miss and hit demonstrated
- You can explain when to use
-vvvvs-vvvv
Code Walkthrough¶
The broken playbook uses misaligned - under tasks — YAML parsers fail before Ansible connects anywhere, which is why syntax-check is the first gate. The inventory miss play shows that a valid playbook can still change zero hosts if groups disagree — always graph inventory before production. The module failure play uses ignore_errors: true so the play continues while -vvv captures the exact command and return structure in cmd_out.
Security Considerations¶
- Redact
-vvvlogs before sharing — they may include passwords passed as module args - Do not disable host key checking permanently when debugging SSH — use jump hosts
- Avoid
--ask-vault-passon shared screens; use--vault-password-filewith restrictive permissions - Limit who can run verbose production plays; logs can expose internal topology
- Treat rescue blocks that mutate systems as audited changes
Common Mistakes¶
Skipping syntax-check because 'it is just a one-line fix'
Fix: run --syntax-check every time; YAML errors are faster to fix locally.
Increasing verbosity without a hypothesis
Fix: know which layer failed (inventory vs SSH vs module) before -vvvv.
Using --limit with a typo in production
Fix: echo resolved hosts with ansible-inventory --list --limit before running.
Best Practices¶
- Keep a team triage doc with command snippets (
syntax-check,ping, inventory graph) - Wire syntax-check into CI so broken YAML never merges
- Use
register+debugwithverbosity: 2for tricky tasks - Snapshot failing
-vvvoutput to the ticket system - After fix, rerun with
--checkwhen modules support it
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
ERROR! Syntax Error | YAML/Jinja | --syntax-check; fix indent; validate templates |
UNREACHABLE! | SSH/network | Manual ssh user@host; check ansible_host, port, bastion |
Permission denied (publickey) | Wrong key/user | ansible_user, --private-key, ssh-agent |
VARIABLE IS UNDEFINED | Missing vars file | Trace precedence; hostvars[inventory_hostname] |
| Works locally, fails in AWX | Different inventory/credential | Compare template settings with CLI -i and -e |
| Hangs mid-play | Waiting for become password | --ask-become-pass or fix sudoers |
Summary¶
Troubleshoot Ansible in layers: syntax, inventory, connectivity, variables, then modules. Capture before/after evidence when fixing incident playbooks. Verbosity -vvv is for understanding task execution — use it with a theory, not as a default.
Interview Questions¶
1. What is your first command when a playbook fails before running any task?
Reveal answer
ansible-playbook --syntax-check (and ansible-lint in CI). YAML and Jinja errors fail locally without touching remote systems.
2. How do you confirm a host is in the correct inventory group?
Reveal answer
ansible-inventory -i inventories/prod --graph or --host hostname to inspect groups and vars resolved for that host.
3. What does UNREACHABLE mean versus FAILED?
Reveal answer
UNREACHABLE means Ansible could not connect (SSH/WinRM/network). FAILED means connection worked but the module/task returned failure — different fixes.
4. When do you use -vvv?
Reveal answer
When you need module arguments, connection details, and structured result JSON for a specific failing task — after syntax and inventory are ruled out.
5. How can a playbook run but change nothing?
Reveal answer
Wrong -i, empty group, --limit typo, or --check mode with tasks that skip changes. Always read PLAY RECAP and host counts.
6. How do you debug undefined variables quickly?
Reveal answer
Run with -vvv, use ansible.builtin.debug: var=variable_name, trace precedence (extra-vars > host_vars > group_vars > role defaults), confirm Vault files decrypt.