What's Actually Happening
Ansible playbook execution times out when tasks take longer than the configured timeout. Long-running commands, slow network connections, or unresponsive hosts cause playbook failures.
The Error You'll See
Task timeout:
```bash $ ansible-playbook site.yml
TASK [Install packages] ************ fatal: [server1]: FAILED! => { "msg": "Command timed out after 30 seconds" } ```
SSH timeout:
TASK [Gather Facts] ***********************************************************
fatal: [server1]: UNREACHABLE! => {
"msg": "Failed to connect to the host via ssh: Connection timed out",
"unreachable": true
}Async task timeout:
TASK [Long running task] *******************************************************
fatal: [server1]: FAILED! => {
"msg": "async task timed out after 3600 seconds"
}Why This Happens
- 1.Long-running commands - Tasks exceed default timeout
- 2.Slow network - High latency or low bandwidth
- 3.SSH connection issues - Network unreachable or firewall
- 4.Heavy operations - Large file transfers or package installs
- 5.Unresponsive hosts - Target system frozen
- 6.Wrong timeout value - Insufficient for operation
Step 1: Identify Timed Out Tasks
```bash # Run with verbose output ansible-playbook site.yml -v
# Run with more verbosity ansible-playbook site.yml -vvv
# Check specific host ansible-playbook site.yml -l server1 -vvv
# Check task details in output: # "timeout": 30 <- default timeout
# Common timeout-prone tasks: # - package installation (yum, apt) # - large file copy # - database operations # - system updates # - template rendering with large files
# Run single task for testing ansible server1 -m shell -a "long-running-command" -B 3600 ```
Step 2: Increase Task Timeout
```yaml # Increase timeout for specific task - name: Install packages ansible.builtin.yum: name: "*" state: latest async: 3600 # Maximum runtime in seconds poll: 60 # Check every 60 seconds # async: 3600, poll: 0 = fire and forget
# Or use timeout for commands - name: Long running command ansible.builtin.command: cmd: /usr/bin/long-operation timeout: 600 # 10 minutes (Ansible 2.10+)
# For shell module - name: Shell command with timeout ansible.builtin.shell: cmd: /usr/bin/slow-command timeout: 300
# In ansible.cfg [defaults] timeout = 30 # SSH timeout (default) task_timeout = 600 # Task timeout (default: 0 = no limit) ```
Step 3: Increase SSH Timeout
```bash # In ansible.cfg [defaults] timeout = 60 # SSH connection timeout (default 10)
[ssh_connection] ssh_args = -o ConnectTimeout=60 -o ServerAliveInterval=30 pipelining = True
# Or via environment variable export ANSIBLE_TIMEOUT=60
# Or in playbook: - name: Play with custom timeout hosts: all gather_facts: yes vars: ansible_timeout: 60
# Check SSH connection ansible all -m ping -e "ansible_timeout=60"
# Test SSH directly ssh -o ConnectTimeout=60 user@server1 ```
Step 4: Use Async for Long Tasks
```yaml # For long-running tasks, use async
- name: Run long operation
- ansible.builtin.command:
- cmd: /usr/bin/long-operation
- async: 3600 # 1 hour maximum
- poll: 0 # Don't wait, run asynchronously
- register: long_task
- name: Check on async task
- ansible.builtin.async_status:
- jid: "{{ long_task.ansible_job_id }}"
- register: job_result
- until: job_result.finished
- retries: 60
- delay: 60 # Check every minute for 1 hour
# For fire-and-forget: - name: Start background process ansible.builtin.command: cmd: /usr/bin/background-service async: 45 poll: 0
# For multiple async tasks: - name: Run multiple tasks async ansible.builtin.command: "{{ item }}" async: 3600 poll: 0 loop: - /usr/bin/task1 - /usr/bin/task2 register: async_tasks
- name: Wait for all tasks
- ansible.builtin.async_status:
- jid: "{{ item.ansible_job_id }}"
- register: job_result
- until: job_result.finished
- retries: 60
- delay: 60
- loop: "{{ async_tasks.results }}"
`
Step 5: Optimize Package Installation
```yaml # Package installation can timeout on slow connections
# WRONG: Update all packages (slow) - name: Update all packages ansible.builtin.yum: name: "*" state: latest # This can take a long time
# BETTER: Update specific packages - name: Update specific packages ansible.builtin.yum: name: - nginx - openssl state: latest async: 1800 poll: 30
# Use cache update separately - name: Update cache ansible.builtin.apt: update_cache: yes async: 600 poll: 30
- name: Install packages
- ansible.builtin.apt:
- name: nginx
- state: present
# Disable GPG check if causing timeout - name: Install package without GPG check ansible.builtin.yum: name: package-name state: present disable_gpg_check: yes ```
Step 6: Optimize File Transfers
```yaml # Large file transfers can timeout
# Use synchronize (rsync) for large files - name: Sync large directory ansible.posix.synchronize: src: /local/path/ dest: /remote/path/ # Uses rsync, more efficient than copy
# For single large file with timeout - name: Copy large file ansible.builtin.copy: src: large_file.tar.gz dest: /tmp/large_file.tar.gz async: 3600 poll: 60
# Use unarchive for archives - name: Extract archive ansible.builtin.unarchive: src: large_file.tar.gz dest: /opt/ remote_src: no async: 1800 poll: 30
# Fetch large files - name: Fetch large file ansible.builtin.fetch: src: /remote/large_file.log dest: /local/backup/ flat: yes async: 1800 poll: 30 ```
Step 7: Fix Network Connectivity
```bash # Check if host is reachable ansible server1 -m ping
# Test SSH connection ssh -v user@server1
# Check network latency ping -c 5 server1
# Check for packet loss mtr server1
# In ansible.cfg, adjust SSH settings [ssh_connection] ssh_args = -o ConnectTimeout=30 -o ServerAliveInterval=15 -o ServerAliveCountMax=3 # ServerAliveInterval: Send keepalive every 15 seconds # ServerAliveCountMax: Disconnect after 3 missed keepalives
# Use ControlPersist for faster reconnection ssh_args = -o ControlMaster=auto -o ControlPersist=60s
# Increase pipelining for performance pipelining = True ```
Step 8: Handle Unreachable Hosts
```yaml # Handle unreachable hosts gracefully
- name: Task that may timeout
- ansible.builtin.command:
- cmd: /usr/bin/some-command
- timeout: 60
- ignore_unreachable: yes
- ignore_errors: yes
- register: result
- name: Handle unreachable
- ansible.builtin.debug:
- msg: "Host was unreachable"
- when: result.unreachable is defined and result.unreachable
# Block with rescue for timeout handling - name: Block with error handling block: - name: Potentially slow task ansible.builtin.command: cmd: /usr/bin/slow-command timeout: 300 rescue: - name: Handle timeout ansible.builtin.debug: msg: "Task timed out, running fallback" - name: Fallback action ansible.builtin.command: cmd: /usr/bin/fallback ```
Step 9: Use Strategies for Timeout Control
```yaml # Use free strategy to not wait for all hosts - name: Run without waiting hosts: all strategy: free # Don't wait for other hosts tasks: - name: Long task ansible.builtin.command: cmd: /usr/bin/long-command async: 3600 poll: 60
# Use linear strategy (default) with batch - name: Batch execution hosts: all strategy: linear serial: 5 # Run on 5 hosts at a time tasks: - name: Task ansible.builtin.command: cmd: /usr/bin/command
# Debug strategy for troubleshooting - name: Debug execution hosts: all strategy: debug # Pauses on failure for interactive debugging ```
Step 10: Monitor Long-Running Playbooks
```bash # Use callback plugins for monitoring
# In ansible.cfg [defaults] callbacks_enabled = profile_tasks, timer
# profile_tasks shows task timing # timer shows overall playbook time
# Run with timing ansible-playbook site.yml
# Output shows: # TASK [Install packages] *********** # Wednesday 16 April 2026 00:37:00 +0000 (0:00:30.123) 0:01:15.456 **** # ^ task duration ^ total time
# Check running processes ansible all -m shell -a "ps aux | grep ansible"
# Monitor async tasks ansible all -m async_status -a "jid=<job_id>" ```
Ansible Timeout Settings Reference
| Setting | Location | Default | Purpose |
|---|---|---|---|
| timeout | ansible.cfg | 10 | SSH connection timeout |
| task_timeout | ansible.cfg | 0 | Task execution timeout |
| async | task | - | Maximum async runtime |
| poll | task | 10 | Async status check interval |
| timeout (task) | task | - | Command timeout (2.10+) |
Verify the Fix
```bash # After increasing timeouts or optimizing tasks
# 1. Run playbook with verbose output ansible-playbook site.yml -v
# 2. Check task timing with profile_tasks ansible-playbook site.yml # Should show task durations
# 3. Verify no timeout errors # Playbook should complete successfully
# 4. Check async tasks completed ansible all -m async_status -a "jid=<job_id>" # Should show finished: true
# 5. Test connectivity ansible all -m ping # All hosts should respond
# 6. Monitor future runs ansible-playbook site.yml --check # Dry run to verify timing ```
Related Issues
- [Fix Ansible Host Unreachable](/articles/fix-ansible-host-unreachable)
- [Fix Ansible Task Failed](/articles/fix-ansible-task-failed)
- [Fix Ansible SSH Connection Failed](/articles/fix-ansible-ssh-connection-failed)