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 ignore_errors: Handle Task Failures (Complete Guide)

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

Complete guide to Ansible ignore_errors. Handle task failures gracefully, use failed_when, rescue blocks, and control error behavior in playbooks.

By default, Ansible stops playbook execution when a task fails. The ignore_errors directive and related error handling features let you control this behavior.

ignore_errors: true

Continue playbook execution even if a task fails:

- name: Check if service exists (may fail)
  ansible.builtin.command: systemctl status myapp
  register: service_check
  ignore_errors: true

- name: Install service if not present ansible.builtin.apt: name: myapp state: present when: service_check.rc != 0

Important: ignore_errors still marks the task as "failed" in the output — it just doesn't stop execution.

See also: Ansible ignore_errors: Error Handling Best Practices (Complete Guide)

ignore_unreachable: true

Specifically handle unreachable hosts (different from task failures):

- name: Check connectivity
  ansible.builtin.ping:
  ignore_unreachable: true
  register: ping_result

- name: Skip unreachable hosts ansible.builtin.debug: msg: "Host is reachable" when: not ping_result.unreachable | default(false)

failed_when: Custom Failure Conditions

Define your own failure criteria:

- name: Run database migration
  ansible.builtin.command: /opt/app/migrate.sh
  register: migration
  failed_when:
    - migration.rc != 0
    - "'already up to date' not in migration.stdout"
- name: Check disk space
  ansible.builtin.command: df -h /
  register: disk_space
  failed_when: "'100%' in disk_space.stdout"

Never fail (always succeed)

- name: Best-effort cleanup
  ansible.builtin.command: rm -f /tmp/lockfile
  failed_when: false

See also: Ansible ignore_errors: Skip Task Failures & Continue Playbook (Guide)

changed_when: Control Changed Status

Prevent false "changed" status:

- name: Check application version
  ansible.builtin.command: /opt/app/version.sh
  register: app_version
  changed_when: false
  failed_when: app_version.rc != 0

block/rescue/always: Try-Catch Pattern

- name: Deploy with rollback
  block:
    - name: Deploy new version
      ansible.builtin.command: /opt/deploy.sh --version={{ new_version }}

- name: Run health check ansible.builtin.uri: url: http://localhost:8080/health status_code: 200

rescue: - name: Rollback on failure ansible.builtin.command: /opt/deploy.sh --version={{ old_version }}

- name: Notify team ansible.builtin.mail: subject: "Deployment failed on {{ inventory_hostname }}" to: ops@example.com

always: - name: Clean up temp files ansible.builtin.file: path: /tmp/deploy-artifacts state: absent

See also: Effective Techniques to Clear Host Errors in Ansible Playbooks

any_errors_fatal: Stop Everything

Stop the entire playbook across all hosts when any host fails:

- name: Critical pre-flight checks
  hosts: all
  any_errors_fatal: true
  tasks:
    - name: Verify disk space
      ansible.builtin.assert:
        that: ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_available') | first > 1073741824
        fail_msg: "Less than 1GB free disk space"

max_fail_percentage

Allow a percentage of hosts to fail before stopping:

- name: Rolling update
  hosts: webservers
  max_fail_percentage: 30
  tasks:
    - name: Update application
      ansible.builtin.command: /opt/update.sh

Practical Examples

Check-and-act pattern

- name: Check if config exists
  ansible.builtin.stat:
    path: /etc/myapp/config.yml
  register: config_file

- name: Generate default config if missing ansible.builtin.template: src: default-config.yml.j2 dest: /etc/myapp/config.yml when: not config_file.stat.exists

Graceful service management

- name: Stop old service (may not exist)
  ansible.builtin.systemd:
    name: myapp-old
    state: stopped
  ignore_errors: true
  register: stop_result

- name: Remove old service file ansible.builtin.file: path: /etc/systemd/system/myapp-old.service state: absent when: stop_result is not failed

FAQ

What's the difference between ignore_errors and failed_when: false?

ignore_errors: true marks the task as failed but continues. failed_when: false means the task is NEVER marked as failed. Use failed_when: false for truly inconsequential commands.

Does ignore_errors work with handlers?

Yes, but a failed task won't trigger handlers even with ignore_errors. Use changed_when to control handler triggering.

Can I ignore errors for an entire block?

Yes: put ignore_errors: true on the block directive.

Basic ignore_errors

- name: Try to stop old service
  ansible.builtin.service:
    name: legacy-app
    state: stopped
  ignore_errors: true

Check Error and Act

- name: Check if service exists
  ansible.builtin.command: systemctl status myapp
  register: service_check
  ignore_errors: true
  changed_when: false

- name: Install if missing ansible.builtin.package: name: myapp state: present when: service_check.rc != 0 become: true

ignore_unreachable

# Continue even if host is down
- name: Check if host is alive
  ansible.builtin.ping:
  ignore_unreachable: true
  register: ping_result

- name: Skip unreachable hosts debug: msg: "Host {{ inventory_hostname }} is reachable" when: ping_result is not unreachable

failed_when (Custom Failure Conditions)

# Don't fail on specific return codes
- name: Check if user exists
  ansible.builtin.command: id username
  register: user_check
  failed_when: user_check.rc not in [0, 1]
  changed_when: false

# Fail on specific output - name: Run health check ansible.builtin.uri: url: http://localhost:8080/health register: health failed_when: "'unhealthy' in health.content"

block/rescue/always (Try-Catch)

- block:
    - name: Deploy new version
      ansible.builtin.git:
        repo: https://github.com/myorg/app.git
        dest: /opt/app
        version: "{{ new_version }}"

- name: Run migrations ansible.builtin.command: ./migrate.sh args: chdir: /opt/app

rescue: - name: Rollback on failure ansible.builtin.git: repo: https://github.com/myorg/app.git dest: /opt/app version: "{{ old_version }}"

- name: Notify team ansible.builtin.uri: url: https://hooks.slack.com/xxx method: POST body: '{"text": "Deployment failed, rolled back"}'

always: - name: Restart service ansible.builtin.service: name: myapp state: restarted become: true

Error Handling Comparison

| Method | Use Case | |--------|----------| | ignore_errors: true | Continue regardless of failure | | failed_when | Custom failure conditions | | block/rescue | Try-catch with rollback | | ignore_unreachable | Handle offline hosts | | any_errors_fatal | Stop ALL hosts on any failure | | max_fail_percentage | Allow some hosts to fail |

any_errors_fatal

- hosts: webservers
  any_errors_fatal: true  # Stop entire play if ANY host fails
  serial: 1
  tasks:
    - name: Critical deployment
      command: ./deploy.sh

FAQ

Does ignore_errors skip handlers?

No — if the task that notified a handler reported changed (even with an error), the handler still runs.

How do I fail the play later based on ignored errors?

- command: /opt/check.sh
  register: result
  ignore_errors: true

# ... more tasks ...

- fail: msg: "Earlier check failed: {{ result.stderr }}" when: result.rc != 0

What's the difference between ignore_errors and failed_when: false?

Both prevent failure, but failed_when: false means the task never fails (always shows ok/changed). ignore_errors: true still marks it as failed in output but continues.

Basic ignore_errors

- ansible.builtin.command: /opt/check-service.sh
  register: result
  ignore_errors: true

- debug: msg: "Check failed but continuing: {{ result.stderr }}" when: result.failed

ignore_errors vs failed_when

# ignore_errors — task "fails" but play continues
- command: /opt/optional-check.sh
  ignore_errors: true

# failed_when — redefine what "failure" means - command: /opt/check.sh register: result failed_when: result.rc not in [0, 1] # rc=0 or rc=1 = success; anything else = failure

Handle Errors with block/rescue

- block:
    - name: Try to deploy
      command: /opt/deploy.sh

- name: Verify deployment uri: { url: "http://localhost/health", status_code: 200 }

rescue: - name: Rollback on failure command: /opt/rollback.sh

- name: Notify team uri: url: https://hooks.slack.com/... method: POST body: '{"text": "Deploy failed, rolled back"}'

always: - name: Clean up temp files file: { path: /tmp/deploy-artifacts, state: absent }

Conditional Error Handling

- command: /opt/check.sh
  register: check
  ignore_errors: true

- name: Handle failure debug: msg: "Check failed with: {{ check.stderr }}" when: check.failed

- name: Continue on success debug: msg: "Check passed: {{ check.stdout }}" when: not check.failed

ignore_unreachable

# Continue even if host is down
- ping:
  ignore_unreachable: true
  register: ping_result

- debug: msg: "Host {{ inventory_hostname }} is unreachable" when: ping_result.unreachable | default(false)

any_errors_fatal

# Stop ALL hosts if ANY host fails
- hosts: webservers
  any_errors_fatal: true
  tasks:
    - name: Critical check
      command: /opt/pre-deploy-check.sh
    # If this fails on ANY host, entire play stops

max_fail_percentage

# Allow up to 20% of hosts to fail
- hosts: webservers
  max_fail_percentage: 20
  serial: 10
  tasks:
    - name: Rolling deploy
      command: /opt/deploy.sh

changed_when with Error Handling

- command: /opt/idempotent-script.sh
  register: result
  changed_when: "'CHANGED' in result.stdout"
  failed_when: result.rc > 1  # Only fail on rc > 1

Common Patterns

# Check if service exists before managing it
- command: systemctl cat myapp
  register: service_check
  ignore_errors: true
  changed_when: false

- service: { name: myapp, state: restarted } when: service_check.rc == 0

# Optional package removal - apt: { name: old-package, state: absent } ignore_errors: true # OK if package not found become: true

# Graceful degradation - uri: url: http://api.example.com/data timeout: 5 register: api_call ignore_errors: true

- set_fact: api_data: "{{ api_call.json if not api_call.failed else {} }}"

FAQ

Does ignore_errors affect handlers?

Yes — if a task with ignore_errors: true notifies a handler and fails, the handler is still triggered (because the play continues).

ignore_errors vs block/rescue?

ignore_errors silently continues. block/rescue lets you execute recovery logic. Use rescue for production workflows.

Does ignore_errors count as a failure?

The task shows as "failed" (red) in output but the play continues. ansible_failed_result is set. Use failed_when: false to not mark it as failed at all.

Basic ignore_errors

- ansible.builtin.command: /opt/check-status.sh
  ignore_errors: true
  register: status_check

Check Result After Ignoring

- command: /opt/check-service.sh
  register: result
  ignore_errors: true

- debug: msg: "Service is down!" when: result.rc != 0

- debug: msg: "Service is running" when: result.rc == 0

failed_when (Better Control)

# Only fail on specific conditions
- command: /opt/deploy.sh
  register: deploy
  failed_when:
    - deploy.rc != 0
    - "'already running' not in deploy.stderr"

ignore_unreachable

# Continue even if host is unreachable
- ping:
  ignore_unreachable: true
  register: ping_result

- debug: msg: "Host {{ inventory_hostname }} is down" when: ping_result.unreachable is defined

Block/Rescue (Try/Catch)

- block:
    - command: /opt/deploy.sh
    - service:
        name: myapp
        state: started
  rescue:
    - debug:
        msg: "Deploy failed, rolling back"
    - command: /opt/rollback.sh
  always:
    - debug:
        msg: "Cleanup complete"

any_errors_fatal

# Stop ALL hosts if ANY host fails
- hosts: webservers
  any_errors_fatal: true
  tasks:
    - command: /opt/critical-check.sh

max_fail_percentage

# Fail play if >25% hosts fail
- hosts: webservers
  max_fail_percentage: 25
  tasks:
    - service:
        name: myapp
        state: started

changed_when with ignore_errors

- command: /opt/check.sh
  register: check
  ignore_errors: true
  changed_when: false  # Never report changed

FAQ

ignore_errors vs failed_when?

ignore_errors continues regardless of failure. failed_when lets you define what constitutes a failure. Prefer failed_when for precise control.

Does ignore_errors affect handlers?

No — handlers still won't run for failed tasks even with ignore_errors. The task is still marked as failed.

How to fail the play later based on ignored errors?

- fail:
    msg: "Critical check failed earlier"
  when: earlier_result.rc != 0

Related Articles

template lookups in Ansiblehandler ordering in AnsibleAnsible when Conditional GuideAnsible Inventory GuideAnsible Ignore Errors Guide

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home