How do you optimize Ansible playbook execution for large inventories with 500+ hosts? What performance tuning strategies exist?
Quick Answer
Enable pipelining, increase forks, use async tasks, enable fact caching, use mitogen strategy plugin, and minimize SSH connections with ControlPersist.
Detailed Answer
Performance Bottlenecks
Ansible's default behavior creates a new SSH connection per task per host. With 500 hosts and 50 tasks, that's 25,000 SSH connections — extremely slow.
Optimization Strategies
1. Increase forks: Default is 5 (parallel hosts). Set forks = 50 in ansible.cfg for 500 hosts. 2. Enable pipelining: Reduces SSH operations per task from 5 to 2 by executing modules without transferring temporary files. Set pipelining = True. 3. SSH multiplexing: ControlPersist reuses SSH connections. Set ssh_args = -o ControlMaster=auto -o ControlPersist=60s. 4. Fact caching: Cache gather_facts results in Redis/JSON file to avoid re-gathering on every run. Facts rarely change between runs. 5. Async tasks: For long-running tasks (package installs), run async and poll later: async: 300, poll: 0. 6. Mitogen strategy: Third-party plugin that replaces Ansible's SSH+transfer approach with a persistent Python interpreter tunnel. 2-7x faster. 7. free strategy: Execute tasks independently per host instead of waiting for all hosts per task.
Quick Wins
- gather_facts: false on playbooks that don't need facts - Use serial: 10 for rolling deployments instead of all-at-once - Avoid shell/command modules when a native module exists
Code Example
# ansible.cfg optimized for 500+ hosts
[defaults]
forks = 50
strategy = free
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o PreferredAuthentications=publickey
# Async task example
- name: Update all packages (async)
yum:
name: '*'
state: latest
async: 600
poll: 0
register: yum_update
- name: Wait for updates to complete
async_status:
jid: "{{ yum_update.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 60
delay: 10
# Free strategy - each host proceeds independently
- hosts: all
strategy: free
tasks:
- name: Deploy application
copy:
src: app.tar.gz
dest: /opt/app/Interview Tip
Mention specific numbers: forks=50 for 500 hosts, ControlPersist=60s, pipelining=True. Quantify the improvement (pipelining alone is 2x faster). The Mitogen strategy plugin is a power-user answer.