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 Run Playbooks in Parallel: Async, Forks & Concurrent Execution

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

How to run Ansible playbooks and tasks in parallel. Use async, forks, free strategy, and GNU parallel to execute multiple playbooks simultaneously.

Introduction

By default, Ansible runs tasks sequentially — one task across all hosts before moving to the next. But many scenarios require parallel execution: running two independent playbooks simultaneously, launching long tasks without waiting, or maximizing throughput across hundreds of hosts.

This guide covers every approach to parallel execution in Ansible, from simple shell backgrounding to advanced async patterns.

See also: Ansible Async: Run Long-Running Tasks in Background (Complete Guide)

Method 1: Run Two Playbooks Simultaneously (Shell)

The simplest approach — run separate ansible-playbook processes:

# Background both playbooks
ansible-playbook deploy-web.yml &
ansible-playbook deploy-db.yml &
wait  # Wait for both to finish
echo "Both playbooks completed"

With output logging:

ansible-playbook deploy-web.yml > /tmp/web.log 2>&1 &
WEB_PID=$!

ansible-playbook deploy-db.yml > /tmp/db.log 2>&1 &
DB_PID=$!

# Wait and check results
wait $WEB_PID
WEB_RC=$?
wait $DB_PID
DB_RC=$?

echo "Web: exit $WEB_RC, DB: exit $DB_RC"

Method 2: GNU Parallel

# Run multiple playbooks in parallel
parallel ansible-playbook {} ::: deploy-web.yml deploy-db.yml configure-monitoring.yml

# With limit on concurrent jobs
parallel -j 3 ansible-playbook {} ::: playbook1.yml playbook2.yml playbook3.yml playbook4.yml

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

Method 3: Async Tasks (Within a Playbook)

Launch tasks without waiting, then poll for results:

---
- hosts: all
  tasks:
    # Launch long-running tasks in parallel
    - name: Run database backup
      command: /opt/scripts/backup-db.sh
      async: 3600    # Max runtime: 1 hour
      poll: 0         # Don't wait — fire and forget
      register: backup_job

    - name: Run log rotation
      command: /opt/scripts/rotate-logs.sh
      async: 600
      poll: 0
      register: logs_job

    - name: Run cache warmup
      command: /opt/scripts/warm-cache.sh
      async: 300
      poll: 0
      register: cache_job

    # Do other work while async tasks run
    - name: Deploy new config
      template:
        src: app.conf.j2
        dest: /etc/myapp/app.conf

    # Now wait for all async tasks
    - name: Wait for backup
      async_status:
        jid: "{{ backup_job.ansible_job_id }}"
      register: backup_result
      until: backup_result.finished
      retries: 60
      delay: 30

    - name: Wait for logs
      async_status:
        jid: "{{ logs_job.ansible_job_id }}"
      register: logs_result
      until: logs_result.finished
      retries: 30
      delay: 10

    - name: Wait for cache
      async_status:
        jid: "{{ cache_job.ansible_job_id }}"
      register: cache_result
      until: cache_result.finished
      retries: 30
      delay: 10

Method 4: Free Strategy

Default strategy (linear) waits for all hosts to finish a task before moving on. free lets each host proceed independently:

---
- hosts: all
  strategy: free
  tasks:
    - name: Update packages (slow on some hosts)
      apt:
        upgrade: dist
      become: true

    - name: Restart service
      service:
        name: myapp
        state: restarted
      become: true

With free, fast hosts don't wait for slow ones.

See also: Meeting Dr. Tony Parmar, Seasoned Healthcare Professional and Author at the New Delhi International Book Fair

Method 5: Increase Forks

# ansible.cfg
[defaults]
forks = 50  # Default is 5!
# Or per-run
ansible-playbook site.yml -f 50

This controls how many hosts are processed simultaneously per task.

Method 6: Multiple Plays in One Playbook

---
# These run sequentially (plays are always sequential)
- name: Deploy web servers
  hosts: webservers
  roles:
    - webserver

- name: Deploy databases
  hosts: dbservers
  roles:
    - database

To make them truly parallel, split into separate files and use shell backgrounding (Method 1).

Method 7: AWX/AAP Workflow Templates

In AWX or Ansible Automation Platform, use Workflow Templates to run playbooks in parallel:

[Provision VMs] → [Deploy Web] ─┐
                                 ├→ [Run Tests]
                  [Deploy DB]  ──┘

Workflow nodes can run simultaneously when they don't depend on each other.

Method 8: Import with Async

---
- hosts: webservers
  tasks:
    - name: Deploy app (async on each host)
      include_tasks: deploy-tasks.yml
      vars:
        async_mode: true

# deploy-tasks.yml
- command: /opt/deploy.sh
  async: 600
  poll: "{{ 0 if async_mode else 15 }}"
  register: deploy_result

Comparison

MethodScopeComplexityBest For
Shell &PlaybooksLow2-3 independent playbooks
GNU parallelPlaybooksLowMany independent playbooks
Async tasksTasks within playMediumLong-running tasks
Free strategyHosts within playLowHeterogeneous host speeds
ForksHosts per taskLowLarge inventories
AWX WorkflowsPlaybooksMediumProduction orchestration

Pitfalls to Avoid

Race Conditions

# DANGEROUS — both tasks modify the same file
- name: Task A writes config
  template: { src: a.j2, dest: /etc/app.conf }
  async: 60
  poll: 0

- name: Task B writes config
  template: { src: b.j2, dest: /etc/app.conf }  # Overwrites A!
  async: 60
  poll: 0

Resource Contention

# Too many forks can overload the controller
# Monitor controller CPU/memory when increasing forks
forks = 100  # May need 4+ GB RAM on controller

Async + become

# Async tasks with become work, but the job runs as the become user
- command: /opt/backup.sh
  async: 3600
  poll: 0
  become: true
  become_user: backup

FAQ

Can I run two plays in the same playbook in parallel?

No — plays within a playbook always run sequentially. To run plays in parallel, put them in separate files and launch with & or GNU parallel.

What happens if an async task fails?

Check the result with async_status. If the task fails, async_status returns finished: true and failed: true. Handle it with failed_when or ignore_errors.

How many forks should I use?

Start with forks = 20-50. Each fork uses ~100MB on the controller. Monitor controller resources and adjust. For SSH, also consider pipelining = true to reduce connections.

Can I mix async and synchronous tasks?

Yes — async tasks run in the background while subsequent synchronous tasks run normally. Use async_status to collect results when needed.

Category: interviews

Browse all Ansible tutorials · AnsiblePilot Home