AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible check_mode: Dry Run & Test Playbooks Without Making Changes

By Luca Berton · Published 2026-04-03 · Category: installation

How to use Ansible check_mode for dry run testing. Run playbooks safely without changes, use --check --diff, test idempotency. Practical YAML playbook examples.

Ansible check mode (--check) lets you preview what changes a playbook would make without actually applying them. It's a dry run — essential for testing playbooks safely in production.

Basic Usage

# Run playbook in check mode
ansible-playbook site.yml --check

# Check mode with diff output ansible-playbook site.yml --check --diff

See also: Ansible vs GitHub Actions: Key Differences & When to Use Each (2026)

How Check Mode Works

In check mode, Ansible: Connects to remote hosts normally Evaluates all conditions and variables Reports what WOULD change — without executing changes Shows "changed" for tasks that would modify the system

Important: Not all modules support check mode. Modules that don't will be skipped with a warning.

Force Check Mode per Task

- name: Always run in check mode (even during real runs)
  ansible.builtin.command: /opt/validate.sh
  check_mode: true

- name: Never run in check mode (skip during dry runs) ansible.builtin.command: /opt/setup-prereqs.sh check_mode: false

See also: New CI Requirement for Ansible Collections: Testing Against Devel Branch (2026)

Use check_mode Variable in Conditions

- name: Show what would happen
  ansible.builtin.debug:
    msg: "Running in check mode - no changes will be made"
  when: ansible_check_mode

- name: Only run during real execution ansible.builtin.command: /opt/irreversible-action.sh when: not ansible_check_mode

Diff Mode: See Exact Changes

ansible-playbook site.yml --diff
ansible-playbook site.yml --check --diff
# Enable diff for specific tasks
- name: Update config with diff
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  diff: true

Output shows unified diff:

--- before: /etc/nginx/nginx.conf
+++ after: /tmp/nginx.conf
@@ -1,3 +1,3 @@
-worker_processes 2;
+worker_processes 4;

See also: ACTION REQUIRED: Ansible Collections Must Add CI Test Runs Against Devel Branch

Check Mode with Registered Variables

- name: Check package status
  ansible.builtin.apt:
    name: nginx
    state: present
  check_mode: true
  register: nginx_check

- name: Report if nginx needs installing ansible.builtin.debug: msg: "Nginx would be installed" when: nginx_check.changed

Modules That Don't Support Check Mode

Some modules skip in check mode: • ansible.builtin.commandansible.builtin.shellansible.builtin.rawansible.builtin.script

Force them to run even in check mode:

- name: Run validation even in check mode
  ansible.builtin.command: /opt/validate-config.sh
  check_mode: false
  register: validation
  changed_when: false

Practical Patterns

Pre-deployment validation

# Step 1: Dry run
ansible-playbook deploy.yml --check --diff

# Step 2: Review output # Step 3: Apply for real ansible-playbook deploy.yml

CI/CD pipeline check

# .gitlab-ci.yml
ansible-check:
  script:
    - ansible-playbook site.yml --check --diff
  only:
    - merge_requests

ansible-apply: script: - ansible-playbook site.yml only: - main

FAQ

Is check mode 100% accurate?

No. Some tasks depend on previous task results that weren't actually applied. Commands modules can't predict output. Use it as a best-effort preview, not a guarantee.

Can I use check mode with ansible ad-hoc commands?

Yes: ansible all -m apt -a "name=nginx state=present" --check

What's the difference between --check and --syntax-check?

--syntax-check only validates YAML/playbook syntax. --check actually connects to hosts and simulates execution.

Run in Check Mode

# Dry run entire playbook
ansible-playbook site.yml --check

# Check mode with diff (show what would change) ansible-playbook site.yml --check --diff

Per-Task Check Mode

Always run (even in check mode)

- name: Gather info (always runs)
  ansible.builtin.command: df -h
  check_mode: false
  changed_when: false
  register: disk_info

Never run in check mode

- name: Dangerous operation
  ansible.builtin.command: /opt/migrate.sh
  check_mode: true  # Only runs in check mode (skipped normally... wait, reversed)

Conditional on check mode

- name: Only in real runs
  ansible.builtin.service:
    name: nginx
    state: restarted
  when: not ansible_check_mode

- name: Report what would happen ansible.builtin.debug: msg: "Would restart nginx" when: ansible_check_mode

Diff Mode

ansible-playbook site.yml --diff

Shows file content changes:

--- /etc/nginx/nginx.conf (before)
+++ /etc/nginx/nginx.conf (after)
@@ -1,3 +1,3 @@
-worker_processes 2;
+worker_processes 4;

Combine check + diff

ansible-playbook site.yml --check --diff

Preview ALL changes without applying them.

CI/CD Validation

# .github/workflows/ansible-lint.yml
- name: Syntax check
  run: ansible-playbook site.yml --syntax-check

- name: Dry run run: ansible-playbook site.yml --check --diff env: ANSIBLE_HOST_KEY_CHECKING: "false"

Modules That Support Check Mode

Most built-in modules support check mode. They report what would change:

| Module | Check Mode Behavior | |--------|-------------------| | copy | Reports if file would change | | template | Shows diff of rendered template | | package | Reports packages to install/remove | | service | Reports state changes | | file | Reports permission/ownership changes | | lineinfile | Reports line changes | | command | Always shows "skipping" (can't predict) |

Force Changed Status

- name: Command that check mode can't predict
  ansible.builtin.command: /opt/deploy.sh
  register: result
  changed_when: "'deployed' in result.stdout"
  check_mode: false  # Always run, even in check mode

FAQ

Why do some tasks show "skipping" in check mode?

Modules like command and shell can't predict their outcome. They skip in check mode unless you set check_mode: false.

Can handlers run in check mode?

No — handlers don't fire in check mode since no actual changes occur.

How do I test a playbook that depends on previous task results?

Use check_mode: false on gathering tasks so subsequent tasks have the data they need:

- name: Get current version
  command: cat /opt/app/VERSION
  register: version
  check_mode: false
  changed_when: false

Run in Check Mode

ansible-playbook site.yml --check
# Or short form
ansible-playbook site.yml -C

Check + Diff Mode

# Show what would change
ansible-playbook site.yml --check --diff
ansible-playbook site.yml -CD

Force Task to Run in Check Mode

# This task always runs, even in check mode
- command: systemctl is-active nginx
  register: nginx_status
  check_mode: false  # Always execute
  changed_when: false

Skip Task in Check Mode

# This task is skipped in check mode
- command: /opt/scripts/deploy.sh
  when: not ansible_check_mode

Detect Check Mode in Tasks

- debug:
    msg: "Running in {{ 'CHECK' if ansible_check_mode else 'NORMAL' }} mode"

- name: Deploy (skip in check mode) command: /opt/deploy.sh when: not ansible_check_mode

- name: Report what would happen debug: msg: "Would deploy version {{ app_version }}" when: ansible_check_mode

Per-Task Check Mode

# Always check (never change)
- apt: name=nginx state=present
  check_mode: true  # Never actually installs

# Always run (even during --check) - setup: check_mode: false # Gather facts regardless

Diff Mode Output

ansible-playbook site.yml --diff
# Shows file changes
TASK [Deploy config] ****
--- before: /etc/nginx/nginx.conf
+++ after: /tmp/nginx.conf
@@ -1,3 +1,3 @@
-worker_processes 4;
+worker_processes 8;
 events {
     worker_connections 1024;

always_run (Deprecated)

# OLD (deprecated)
- command: uptime
  always_run: true

# NEW - command: uptime check_mode: false

Practical Patterns

Validate Before Apply

# Step 1: Check what would change
ansible-playbook site.yml -CD

# Step 2: Review output

# Step 3: Apply changes ansible-playbook site.yml

Audit Mode Playbook

---
- name: Security audit (check mode only)
  hosts: all
  tasks:
    - name: Check for unpatched packages
      apt:
        upgrade: dist
      check_mode: true
      register: updates

- debug: msg: "{{ updates.stdout_lines | select('match', '^Inst') | list | length }} updates available" when: updates.changed

Modules That Support Check Mode

Most modules support check mode. Notable exceptions: • command / shell / raw — can't predict changes • script — can't predict outcome • Custom modules — depends on implementation

FAQ

Does check mode guarantee no changes?

No — modules that don't support check mode will be skipped (not simulated). Some modules may still make read-only API calls.

Can I enforce check mode for safety?

# ansible.cfg
[defaults]
# No setting to force check mode globally
# Use wrapper script or CI pipeline enforcement

Why does my playbook fail in check mode?

Tasks that register variables may return empty results in check mode, causing downstream tasks to fail. Use check_mode: false for prerequisite tasks or add when: result is not skipped.

Run in Check Mode

ansible-playbook site.yml --check

Check + Diff

ansible-playbook site.yml --check --diff
# Shows what would change AND the actual diff

Per-Task Check Mode

# Always run (even in check mode)
- command: cat /etc/os-release
  check_mode: false
  register: os_info
  changed_when: false

# Always check (even in normal mode) - apt: name: nginx state: present check_mode: true # Never actually installs register: would_change

- debug: msg: "nginx needs installing" when: would_change.changed

Handle Tasks That Don't Support Check Mode

- command: /opt/deploy.sh
  when: not ansible_check_mode  # Skip in check mode

# Or use check_mode: false to force execution - command: cat /opt/app/version check_mode: false register: current_version changed_when: false

Diff Mode in Playbook

- template:
    src: config.j2
    dest: /etc/myapp/config.conf
  diff: true  # Always show diff for this task

Practical Patterns

# Validate before applying
# Step 1: Check mode
ansible-playbook site.yml --check --diff

# Step 2: Review output # Step 3: Apply if satisfied ansible-playbook site.yml

Register in Check Mode

# Some modules report what WOULD change
- apt:
    name: nginx
    state: present
  check_mode: true
  register: apt_check

- debug: msg: "{{ apt_check.changed | ternary('Would install', 'Already installed') }}"

check_mode Variable

# ansible_check_mode is true during --check
- debug:
    msg: "Running in check mode"
  when: ansible_check_mode

- template: src: dangerous-config.j2 dest: /etc/critical/config when: not ansible_check_mode # Skip in dry run

FAQ

Does --check make any changes?

No — most modules simulate the action and report what would change. Some modules (like command) just skip.

Can I check specific tasks only?

Use check_mode: true on individual tasks. Or use --check with --tags to limit scope.

What does "skipping" mean in check mode?

Some modules can't simulate (e.g., command, shell). They skip unless you set check_mode: false.

Related Articles

Ansible template guidetask gating with Ansible whenNginx vhost provisioning with AnsibleAnsible assert Module Guide

Related: GitHub Ansible Requires Signed Commits

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home