Check Mode and Diff Mode¶
--check runs a playbook without changing anything, and --diff shows the exact lines that would change. Run both together for a dry run you can read.
What You'll Learn¶
- What
--checkactually does, and its limits - What
--diffadds on top of it - Why
changed_when/failed_whenmatter for check mode withcommand/shell
Mental Model¶
--check runs every task's check-then-act comparison without performing the act — each module reports what it would do, but the managed node is never touched. --diff adds a before/after view of any file content that would change.
TASK [Deploy nginx configuration] ***
--- before: /etc/nginx/nginx.conf
+++ after: nginx.conf.j2
@@ -12,7 +12,7 @@
-worker_connections 768;
+worker_connections 1024;
changed: [web01]
This ran with zero actual changes to web01 — exactly the review step you want before applying an unfamiliar playbook to production.
The Limit: Not Every Module Supports Check Mode¶
A module has to be written to support it (supports_check_mode=True internally, see Build a Custom Module). Most ansible.builtin modules do. command and shell cannot know what they'd change without actually running, so in check mode Ansible skips them. The exception is a task with creates: or removes:, which Ansible can evaluate without running the command.
That skip causes a real problem for read-only commands whose output later tasks need:
- name: Read the currently deployed release
ansible.builtin.command: readlink /opt/checkout/current
register: current_release
changed_when: false
check_mode: false # safe to run for real: it only reads
- name: Upgrade when the release differs
ansible.builtin.include_tasks: upgrade.yml
when: (current_release.stdout | basename) != app_release
Without check_mode: false, the first task is skipped under --check, current_release.stdout doesn't exist, and the dry run fails on the second task, even though a real run would work. Use check_mode: false only on tasks that genuinely change nothing.
changed_when overrides how a command/shell task reports change (by default it reports changed any time its return code is 0, which is almost never what you want) — see it used the same way for idempotency workarounds in command vs. shell vs. raw vs. script. It does not make the task run in check mode.
The opposite control also exists: check_mode: true on a task makes it always dry-run, even in a normal run. That's useful for a "what would change" report task inside a real deployment.
ansible_check_mode is a variable that's true during --check. Use it to skip steps that can't work in a dry run, such as unpacking an archive the previous (dry-run) task never downloaded: when: not ansible_check_mode.
Common Mistakes¶
- Trusting
--checkoutput for a playbook full of unguardedcommand/shelltasks — they're silently skipped, so the dry run doesn't actually preview what they'd do. - Never running
--check --diffbefore a first production run of a new or edited playbook. - Assuming
--checkcatches logic errors like a badwhen:— it only tells you what modules would change, not whether your playbook's logic is correct.
Interview Questions¶
- What does
--checkactually guarantee, and what does it not catch? - Why are
command/shelltasks skipped in check mode by default? - What does
--diffadd on top of--check?
Related¶
Next¶
Continue to Idempotency — the concept this entire section has been building toward.