Check Mode and Debugging
Scope: answering "what would this do?" before it does it, and finding out why when it does something else.
Check mode: the dry run
--check runs the playbook in simulation: modules report
what would change and change nothing.
The lab's proof: with the inventory's app_port edited
from 8080 to 9090 on a scratch copy, --check --diff
predicted the template change line by line —
ansible-playbook site.yml -i inventory-newport.ini --check --diff
-port = 8080
+port = 9090
changed: [web1]
changed: [web2]
— and the actual file on disk still said port = 8080
afterwards. Predicted, diffed, not applied.
Honest limits of the simulation:
- Modules that run raw commands (
command,shell) generally skip in check mode — Ansible can't simulate arbitrary shell — so a check run can be quieter than the real one. - Later tasks may depend on earlier changes (a task that reads a file an earlier task creates). In check mode the file never gets created, so downstream tasks can fail in simulation yet pass live. A check-mode failure is information, not verdict.
- Pair
--checkwith--difffor file tasks; without the diff you get "something would change" but not what.
Discipline: any playbook that touches a machine you care
about gets a --check --diff run first, read by a human.
This is the moral equivalent of the backup guide's
restore drill: rehearse the dangerous thing safely.
Seeing more
-v…-vvvv— increasing verbosity: module arguments, then connection details, then SSH-level tracing.-vis the everyday one; it expands task results to include the module's full return values.ansible-playbook --syntax-check site.yml— parse only. Catches YAML and structure errors in a second, without touching a host (see Troubleshooting for what its failures look like).--list-tasks/--list-hosts— what would run, and where, without running it.--start-at-task "Name"— resume a long playbook at a named task while developing it.
The debug module, used properly
debug prints variables mid-run — the print statement
of Ansible:
- name: What port did this host actually get?
ansible.builtin.debug:
var: app_port
Two mortal mistakes to skip: debugging with shell: echo
(which reports CHANGED and pollutes recaps), and leaving
noisy debug tasks in the finished playbook. The other
debugging instrument is failing deliberately: the fail
module with a when: condition turns "weird state
discovered late" into "stopped early, with a message."
When a task fails
Ansible stops that host at the failed task (other hosts
continue — see --limit rescue patterns in
Running a fleet). Read the failure
from the bottom up: the fatal: line carries the module's
own message, usually precise. Before rerunning blindly,
remember the standing advice: playbooks are idempotent,
so fix the cause and rerun the whole thing. Partial
re-runs (--start-at-task) are for development, not for
operations.