You've started a playbook run, and now you're watching the cursor blink. And blink. And blink some more. Finally, after what feels like forever: FAILED! => {"msg": "timed out"}. Connection timeouts are among the most frustrating Ansible issues because they can stem from so many different causes.

Let's systematically diagnose and fix these timeout problems.

Understanding the Error

Connection timeout errors manifest in different ways:

bash
TASK [Gather Facts] ***********************************************************
fatal: [webserver01]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.1.100 port 22: Connection timed out", "unreachable": true}

Or during command execution:

bash
fatal: [webserver01]: FAILED! => {"msg": "Timeout (12s) waiting for privilege escalation prompt: "}

The first indicates network connectivity issues. The second suggests problems with command execution or privilege escalation.

Step 1: Identify the Timeout Type

First, determine where the timeout occurs:

bash
# Test basic SSH connectivity
time ssh -o ConnectTimeout=10 ansible_user@192.168.1.100 'echo connected'

If this times out, you have a network issue. If it succeeds quickly, the problem is with Ansible's SSH configuration or command execution.

Step 2: Check Network Connectivity

Run basic network diagnostics:

```bash # Test reachability ping -c 5 192.168.1.100

# Check if port is open nc -zv 192.168.1.100 22 # Or with timeout timeout 5 bash -c 'cat < /dev/null > /dev/tcp/192.168.1.100/22 && echo "Port open" || echo "Port closed/filtered"' ```

For cloud instances, verify security groups and network ACLs allow your IP:

```bash # For AWS, check security group rules aws ec2 describe-security-groups --group-ids sg-xxxxx --query 'SecurityGroups[0].IpPermissions'

# The port should show your IP or 0.0.0.0/0 ```

Step 3: Adjust Ansible Timeout Settings

Ansible's default timeout is 10 seconds, which might be too short for slow networks. Increase it in your ansible.cfg:

```ini [defaults] timeout = 60

[ssh_connection] ssh_args = -o ConnectTimeout=60 -o ControlMaster=auto -o ControlPersist=60s ```

Or at runtime:

bash
ansible-playbook -i inventory deploy.yml --timeout 60

For specific tasks, use async for long-running operations:

yaml
- name: Long running operation
  command: /opt/app/slow-install.sh
  async: 3600
  poll: 30

Step 4: Optimize SSH Control Path

SSH Control Master creates persistent connections but can cause timeout issues. Check for stale connections:

```bash # List existing control sockets ls -la ~/.ansible/cp/

# Remove stale sockets rm -rf ~/.ansible/cp/* ```

Adjust your SSH connection settings:

ini
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=300s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r
pipelining = True

The ControlPersist value (300s) determines how long the connection stays open after the last task. Increase for multiple playbooks, decrease if connections go stale.

Step 5: Debug with Verbose SSH Output

See exactly what SSH is doing:

bash
ansible webserver01 -i inventory -m ping -vvv 2>&1 | grep -A 5 "timeout"

For even more detail, test SSH directly with maximum verbosity:

bash
ssh -vvv -o ConnectTimeout=30 ansible_user@192.168.1.100

Look for clues in the output: - debug1: connect to address ... port 22: Connection timed out - Network issue - debug1: Authentications that can continue: publickey - Key not accepted yet - debug1: Waiting for server authentication - Server slow to respond

Step 6: Handle Slow DNS Resolution

If DNS resolution is slow, SSH waits during connection. Bypass DNS for SSH:

bash
# Test if DNS is the culprit
time nslookup webserver01

If slow, add to ansible.cfg:

ini
[ssh_connection]
ssh_args = -o ConnectTimeout=60

And use IP addresses in inventory, or add to /etc/hosts:

bash
echo "192.168.1.100 webserver01" | sudo tee -a /etc/hosts

Step 7: Fix Privilege Escalation Timeouts

Timeouts during become operations need different handling:

yaml
- name: Task requiring sudo
  become: yes
  apt:
    name: nginx
    state: present

If this times out waiting for sudo password, either:

Option A: Configure passwordless sudo:

bash
# On target host
echo "ansible_user ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/ansible_user

Option B: Provide password in playbook:

bash
ansible-playbook -i inventory deploy.yml --ask-become-pass

Option C: Store in vault:

yaml
ansible_become_password: "{{ vault_become_password }}"

Increase become timeout:

ini
[privilege_escalation]
become_timeout = 60

Step 8: Handle Network Latency

For high-latency networks (cross-region, satellite), adjust multiple timeouts:

```ini [defaults] timeout = 120 gather_timeout = 60

[ssh_connection] ssh_args = -o ConnectTimeout=120 -o ServerAliveInterval=30 -o ServerAliveCountMax=5 pipelining = True retries = 3 ```

The ServerAliveInterval keeps connections alive during idle periods.

Step 9: Optimize Fact Gathering

Fact gathering can timeout on systems with many network interfaces or slow disks:

ini
[defaults]
gather_timeout = 60
gathering = smart
fact_caching = jsonfile
fact_caching_timeout = 86400
fact_caching_connection = /tmp/ansible_facts

Or skip fact gathering entirely when not needed:

yaml
- name: Quick task
  hosts: all
  gather_facts: no
  tasks:
    - name: Simple command
      command: echo "done"

Step 10: Handle Proxy and Bastion Hosts

If connecting through a bastion host, timeouts compound. Configure SSH proxy:

ini
[ssh_connection]
ssh_args = -o ProxyCommand="ssh -W %h:%p -q bastion.example.com"

Or in inventory:

yaml
all:
  hosts:
    webserver01:
      ansible_host: 192.168.1.100
      ansible_ssh_common_args: '-o ProxyCommand="ssh -W %h:%p -q bastion.example.com"'

Increase timeouts for proxied connections:

```ini [defaults] timeout = 120

[ssh_connection] ssh_args = -o ConnectTimeout=120 -o ServerAliveInterval=60 ```

Step 11: Monitor Connection Performance

Use timing callbacks to identify slow tasks:

bash
export ANSIBLE_CALLBACK_WHITELIST=profile_tasks
ansible-playbook -i inventory deploy.yml

Output shows which tasks take longest:

bash
Friday 04 April 2026  10:00:00 +0000 (0:00:15.234)    0:00:15.234 ****
TASK [Gather Facts] ***********************************************************

Quick Verification

Test your fixes:

```bash # Basic connectivity ansible all -i inventory -m ping -T 60

# With timing time ansible all -i inventory -m ping ```

Successful output:

bash
webserver01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Prevention Checklist

Add to your ansible.cfg:

```ini [defaults] timeout = 60 gather_timeout = 60 gathering = smart fact_caching = jsonfile fact_caching_timeout = 3600

[ssh_connection] pipelining = True ssh_args = -o ControlMaster=auto -o ControlPersist=300s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r retries = 3

[privilege_escalation] become_timeout = 60 ```

Connection timeouts require patience and systematic debugging. Start with basic network tests, adjust timeouts, optimize SSH settings, and configure appropriate caching for your environment.