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

MethodUse Case
ignore_errors: trueContinue regardless of failure
failed_whenCustom failure conditions
block/rescueTry-catch with rollback
ignore_unreachableHandle offline hosts
any_errors_fatalStop ALL hosts on any failure
max_fail_percentageAllow 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

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home