What is a Dry Run?

A dry run lets you see what changes Ansible would make — without actually changing anything. Use --check to simulate and --diff to see the exact differences.

# Dry run — show what would change
ansible-playbook playbook.yml --check

# Dry run with diff — show exact changes
ansible-playbook playbook.yml --check --diff

--check (Check Mode)

Check mode simulates the playbook run. Tasks report what they would do:

ansible-playbook deploy.yml --check

Output:

TASK [Install nginx] ***
changed: [web1]     # Would install nginx (not actually installed)

TASK [Copy config] ***
changed: [web1]     # Would copy the file (not actually copied)

TASK [Start nginx] ***
ok: [web1]          # Already running, no change needed

Important Limitations

Not all modules support check mode. Modules that don't will either:

  • Skip the task (with a warning)
  • Run normally (ignoring check mode)

The command and shell modules always show "skipped" in check mode because Ansible can't predict their output:

TASK [Run migration script] ***
skipping: [web1]    # Can't simulate command output

--diff (Diff Mode)

Shows the exact line-by-line differences for file changes:

ansible-playbook deploy.yml --diff

Output:

TASK [Deploy nginx config] ***
--- before: /etc/nginx/nginx.conf
+++ after: /tmp/nginx.conf.j2
@@ -1,5 +1,5 @@
 server {
-    listen 80;
+    listen 8080;
     server_name example.com;
 }
changed: [web1]

Combining --check and --diff

The most useful combination — see what would change and the exact differences:

ansible-playbook deploy.yml --check --diff

This is the recommended pre-deployment check:

# 1. Preview changes
ansible-playbook deploy.yml --check --diff

# 2. Review the output carefully

# 3. Apply for real
ansible-playbook deploy.yml

Per-Task Check Mode Control

Force a task to always run (even in check mode)

- name: Gather current state (always runs)
  ansible.builtin.command: cat /opt/app/version.txt
  register: current_version
  check_mode: false  # Runs even during --check

Force a task to always use check mode

- name: Verify config syntax (never changes)
  ansible.builtin.command: nginx -t
  check_mode: true  # Always simulates, even without --check

Using ansible_check_mode Variable

- name: Run migration only in real mode
  ansible.builtin.command: /opt/app/migrate.sh
  when: not ansible_check_mode

- name: Show what would happen
  ansible.builtin.debug:
    msg: "Would run migration (skipped in check mode)"
  when: ansible_check_mode

Practical Workflow

Safe Deployment Process

# Step 1: Lint the playbook
ansible-lint deploy.yml

# Step 2: Dry run with diff
ansible-playbook deploy.yml --check --diff -v

# Step 3: Deploy to staging
ansible-playbook deploy.yml -i staging -v

# Step 4: Verify staging

# Step 5: Deploy to production
ansible-playbook deploy.yml -i production -v

CI/CD Integration

# GitHub Actions example
jobs:
  validate:
    steps:
      - name: Dry run
        run: ansible-playbook deploy.yml --check --diff
        
  deploy:
    needs: validate
    steps:
      - name: Deploy
        run: ansible-playbook deploy.yml

Tips

  1. Always dry-run before production — --check --diff is your safety net
  2. Not all modules support check mode — command, shell, raw can't simulate
  3. Use check_mode: false for data-gathering tasks that must run
  4. Diff only works on file tasks — template, copy, lineinfile, etc.
  5. Combine with --limit to dry-run against a single host first

Learn more safe automation practices in our 800+ tutorials on AnsibleByExample.