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 Async: Run Long-Running Tasks in Background (Complete Guide)

By Luca Berton · Published 2024-01-01 · Category: troubleshooting

Complete guide to Ansible async and poll. Run long-running tasks in the background, implement fire-and-forget patterns, parallel execution, check job status.

The async keyword runs Ansible tasks in the background, allowing long-running operations to continue without blocking the playbook. Combined with poll, it enables fire-and-forget patterns, parallel execution, and timeout control.

How Async Works

- name: Long-running task
  ansible.builtin.command: /opt/scripts/build.sh
  async: 3600    # Maximum runtime in seconds (1 hour)
  poll: 10       # Check every 10 seconds
  • async: N — Maximum allowed runtime in seconds. Task fails if it exceeds this.
  • poll: N — How often (seconds) to check if the task is done.
  • poll: 0 = Fire and forget (don't wait)
  • poll: N (N > 0) = Wait and check every N seconds

Basic Patterns

Wait with Polling (Default)

# Runs in background, checks every 30 seconds, max 1 hour
- name: Run database backup
  ansible.builtin.command: /opt/scripts/backup-database.sh
  async: 3600
  poll: 30

Fire and Forget

# Start task and move on immediately
- name: Start long-running data migration
  ansible.builtin.command: /opt/scripts/migrate-data.sh
  async: 7200
  poll: 0
  register: migration_job

# ... other tasks run while migration continues ...

# Check status later
- name: Check migration status
  ansible.builtin.async_status:
    jid: "{{ migration_job.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 120
  delay: 60

See also: Ansible Run Playbooks in Parallel: Async, Forks & Concurrent Execution

Parallel Execution

Run Tasks on Multiple Hosts Simultaneously

# Fire and forget on all hosts, then wait for all to finish
- name: Start package update on all hosts
  ansible.builtin.yum:
    name: '*'
    state: latest
  async: 1800
  poll: 0
  register: yum_update

- name: Wait for updates to complete
  ansible.builtin.async_status:
    jid: "{{ yum_update.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 60
  delay: 30

Parallel Tasks on Same Host

# Start multiple independent tasks
- name: Download large file 1
  ansible.builtin.get_url:
    url: https://example.com/large-file-1.tar.gz
    dest: /tmp/file1.tar.gz
  async: 600
  poll: 0
  register: download1

- name: Download large file 2
  ansible.builtin.get_url:
    url: https://example.com/large-file-2.tar.gz
    dest: /tmp/file2.tar.gz
  async: 600
  poll: 0
  register: download2

- name: Start compilation
  ansible.builtin.command: make -j4
  args:
    chdir: /opt/source
  async: 1800
  poll: 0
  register: compile_job

# Wait for all to finish
- name: Wait for downloads and compilation
  ansible.builtin.async_status:
    jid: "{{ item.ansible_job_id }}"
  register: async_results
  until: async_results.finished
  retries: 60
  delay: 15
  loop:
    - "{{ download1 }}"
    - "{{ download2 }}"
    - "{{ compile_job }}"

async_status Module

Check on fire-and-forget jobs:

- name: Start background job
  ansible.builtin.command: /opt/scripts/process.sh
  async: 3600
  poll: 0
  register: bg_job

- name: Do other work
  ansible.builtin.debug:
    msg: "Doing other tasks while job runs..."

- name: Check job status
  ansible.builtin.async_status:
    jid: "{{ bg_job.ansible_job_id }}"
  register: job_status

- name: Show status
  ansible.builtin.debug:
    msg: |
      Finished: {{ job_status.finished }}
      Started: {{ job_status.started }}
      {% if job_status.finished %}
      Return code: {{ job_status.rc }}
      {% endif %}

# Wait until complete
- name: Wait for job to finish
  ansible.builtin.async_status:
    jid: "{{ bg_job.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 60
  delay: 30
  # Checks every 30 seconds, up to 30 minutes

See also: Ansible async and poll: Run Long Tasks Without Timeout Complete Guide

Real-World Patterns

Long Package Updates Without SSH Timeout

# SSH connections may time out during long operations
# async keeps the task running server-side even if SSH disconnects
- name: Upgrade all packages (may take 30+ minutes)
  ansible.builtin.yum:
    name: '*'
    state: latest
  async: 3600    # Allow up to 1 hour
  poll: 60       # Check every minute

Parallel Service Restarts

- name: Restart services in parallel
  ansible.builtin.systemd:
    name: "{{ item }}"
    state: restarted
  async: 120
  poll: 0
  register: restart_jobs
  loop:
    - nginx
    - php-fpm
    - redis

- name: Wait for all restarts
  ansible.builtin.async_status:
    jid: "{{ item.ansible_job_id }}"
  register: restart_results
  until: restart_results.finished
  retries: 12
  delay: 10
  loop: "{{ restart_jobs.results }}"

Reboot and Reconnect

- name: Reboot the server
  ansible.builtin.command: shutdown -r now
  async: 1
  poll: 0
  ignore_errors: true

- name: Wait for server to come back
  ansible.builtin.wait_for_connection:
    connect_timeout: 10
    sleep: 5
    delay: 30
    timeout: 300

Background Monitoring During Deployment

- name: Start log monitoring in background
  ansible.builtin.shell: "tail -f /var/log/myapp/app.log > /tmp/deploy-log.txt 2>&1 &"
  async: 600
  poll: 0
  register: monitor

- name: Deploy application
  ansible.builtin.include_role:
    name: deploy

- name: Stop monitoring
  ansible.builtin.command: "kill $(cat /tmp/deploy-monitor.pid)"
  ignore_errors: true

- name: Show deployment logs
  ansible.builtin.command: cat /tmp/deploy-log.txt
  register: deploy_logs
  changed_when: false

- name: Display logs
  ansible.builtin.debug:
    var: deploy_logs.stdout_lines

Timeout Handling

# Task fails if it exceeds async timeout
- name: Process with timeout
  ansible.builtin.command: /opt/scripts/process.sh
  async: 300    # 5 minute maximum
  poll: 10
  register: result
  ignore_errors: true

- name: Handle timeout
  ansible.builtin.debug:
    msg: "Process timed out — cleaning up"
  when: result is failed

- name: Cleanup on timeout
  ansible.builtin.command: /opt/scripts/cleanup.sh
  when: result is failed

See also: Ansible 2.17.0-rc1: Elevating Automation with ‘Gallows Pole’

Limitations

  1. Not all modules support async — Modules that need persistent connections (e.g., synchronize) may not work
  2. No async with with_items/loop — Each loop iteration runs sequentially; use fire-and-forget + async_status loop instead
  3. Temp files on remote hosts — Async creates ~/.ansible_async/ files that need cleanup
  4. become may not work with poll: 0 — Some privilege escalation methods conflict with backgrounding

FAQ

What does async do in Ansible?

async runs a task in the background with a maximum time limit. It prevents SSH timeouts for long operations and enables fire-and-forget patterns where you start a task and check its status later.

What is the difference between async with poll 0 and poll N?

poll: 0 (fire-and-forget) starts the task and immediately continues to the next task. poll: N (N > 0) starts the task and waits, checking every N seconds until it completes.

How do I run tasks in parallel with Ansible?

Use async with poll: 0 to start multiple tasks without waiting, then use async_status in a loop to wait for all jobs to complete. This runs tasks concurrently instead of sequentially.

What happens when an async task times out?

The task is killed on the remote host and Ansible reports a failure. Use ignore_errors: true if you want to handle timeouts gracefully with cleanup tasks.

Can I use async with loops?

Not directly — each loop iteration is still sequential. Instead, use poll: 0 inside the loop to start all iterations as fire-and-forget jobs, then use async_status with a second loop to wait for all results.

Conclusion

  • async: N + poll: M — Run with timeout, check every M seconds
  • async: N + poll: 0 — Fire and forget, check later with async_status
  • Prevents SSH timeouts for long operations
  • Enables parallel execution with fire-and-forget + async_status loops
  • Always set a reasonable async timeout to avoid zombie tasks

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home