Introduction

The "UNREACHABLE" error is one of the most common Ansible failures. When Ansible cannot establish an SSH connection to a managed host, the entire playbook execution fails. This issue can stem from SSH service problems, authentication failures, network connectivity issues, firewall restrictions, or misconfigured inventory settings.

Debugging SSH connectivity issues requires checking multiple layers: network reachability, SSH service status, authentication configuration, and Ansible's own connection settings.

Symptoms

Connection refused errors:

bash
$ ansible web-server -m ping
web-server | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: ssh: connect to host 10.0.1.50 port 22: Connection refused",
    "unreachable": true
}

Permission denied errors:

bash
$ ansible web-server -m ping
web-server | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Permission denied (publickey,password).",
    "unreachable": true
}

Host key verification failures:

bash
$ ansible web-server -m ping
web-server | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Host key verification failed.",
    "unreachable": true
}

Connection timeout errors:

bash
$ ansible web-server -m ping -vvv
web-server | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: ssh: connect to host 10.0.1.50 port 22: Connection timed out",
    "unreachable": true
}

SSH key file errors:

bash
$ ansible web-server -m ping
web-server | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: no such identity: /home/user/.ssh/wrong_key: No such file or directory",
    "unreachable": true
}

Wrong user or port:

bash
$ ansible web-server -m ping -vvv
web-server | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Permission denied, please try again.",
    "unreachable": true
}
# But verbose shows:
# SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o 'IdentityFile="/home/user/.ssh/id_rsa"' -o KbdInteractiveAuthentication=no -o 'User="wronguser"' -o 'Port=22"' ...

Common Causes

1. SSH Service Not Running

The target host's SSH daemon is stopped or not installed:

bash
$ ssh target-host
ssh: connect to host target-host port 22: Connection refused

2. Firewall Blocking Port 22

Network firewall or host firewall blocks SSH:

```bash $ telnet target-host 22 telnet: Unable to connect to remote host: Connection timed out

$ nmap -p 22 target-host PORT STATE SERVICE 22/tcp filtered ssh # filtered = blocked by firewall ```

3. SSH Key Not Deployed

Public key not in authorized_keys on target:

bash
$ ssh -i ~/.ssh/ansible_key target-host
Permission denied (publickey)

4. Wrong SSH User

Inventory specifies wrong username:

```ini # Inventory has: [target_servers] server-01 ansible_user=ubuntu

# But target uses: # user: admin ```

5. Non-Standard SSH Port

SSH listening on different port:

```bash $ ssh target-host ssh: connect to host target-host port 22: Connection refused

$ ssh -p 2222 target-host # Works! ```

6. Host Key Changed

Target was rebuilt, changing host keys:

bash
$ ssh target-host
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!

7. Network Routing Issues

Target not reachable from control node:

bash
$ ping target-host
PING target-host (10.0.2.50): 56 data bytes
Request timeout for icmp_seq 0
# No route or routing issue

Step-by-Step Fix

Step 1: Verify Basic Network Connectivity

Check if host is reachable:

```bash # Ping the host ping -c 3 target-host

# Check if port is open nc -zv target-host 22 # Or: telnet target-host 22

# Use nmap for detailed info nmap -p 22 target-host

# Check route traceroute target-host ```

Step 2: Test SSH Manually

Test SSH connection directly:

```bash # Basic SSH test ssh -v target-host

# With specific user ssh -v user@target-host

# With specific key ssh -v -i ~/.ssh/ansible_key user@target-host

# With specific port ssh -v -p 2222 user@target-host

# Full verbose test matching Ansible options ssh -v -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \ -i ~/.ssh/ansible_key user@target-host ```

Step 3: Verify SSH Service on Target

Check SSH service status:

```bash # If you have access via console or different method ssh target-host "systemctl status sshd" ssh target-host "systemctl status ssh"

# Check if listening ssh target-host "ss -tlnp | grep :22" ssh target-host "netstat -tlnp | grep :22"

# Check SSH config ssh target-host "cat /etc/ssh/sshd_config | grep Port" ssh target-host "cat /etc/ssh/sshd_config | grep PermitRootLogin" ```

Step 4: Fix Inventory Configuration

Correct inventory SSH settings:

```ini # inventory.ini [target_servers] # Basic format server-01 ansible_host=10.0.1.50

# With explicit SSH settings server-02 ansible_host=10.0.1.51 ansible_user=admin ansible_port=2222 ansible_ssh_private_key_file=~/.ssh/ansible_key

# Group variables [target_servers:vars] ansible_user=ansible ansible_ssh_private_key_file=~/.ssh/ansible_key ansible_ssh_common_args='-o StrictHostKeyChecking=no'

# For bastion host access [private_servers] private-01 ansible_host=10.0.2.50 ansible_ssh_common_args='-o ProxyCommand="ssh -W %h:%p bastion.example.com"' ```

YAML inventory format:

yaml
# inventory.yml
all:
  hosts:
    server-01:
      ansible_host: 10.0.1.50
      ansible_user: admin
      ansible_port: 22
      ansible_ssh_private_key_file: ~/.ssh/ansible_key
  children:
    webservers:
      hosts:
        server-01:
      vars:
        ansible_user: webadmin
    dbservers:
      hosts:
        server-02:
        server-03:

Step 5: Deploy SSH Keys

Set up SSH key authentication:

```bash # Generate key if not exists ssh-keygen -t ed25519 -f ~/.ssh/ansible_key -N ""

# Copy key to target ssh-copy-id -i ~/.ssh/ansible_key.pub user@target-host

# Or with password authentication ssh-copy-id -i ~/.ssh/ansible_key.pub user@target-host

# Verify key works ssh -i ~/.ssh/ansible_key user@target-host "echo 'SSH key works'" ```

Deploy key with Ansible (bootstrapping):

```yaml # deploy_ssh_key.yml - name: Deploy SSH key hosts: all gather_facts: false vars: ssh_key: "{{ lookup('file', '~/.ssh/ansible_key.pub') }}"

tasks: - name: Create user if not exists user: name: ansible shell: /bin/bash create_home: yes become: true

  • name: Deploy SSH authorized key
  • authorized_key:
  • user: ansible
  • key: "{{ ssh_key }}"
  • state: present
  • become: true
  • `

Step 6: Handle Host Key Verification

Fix host key issues:

```bash # Remove old host key ssh-keygen -R target-host # Or for IP: ssh-keygen -R 10.0.1.50

# Disable host key checking temporarily export ANSIBLE_HOST_KEY_CHECKING=False ansible all -m ping

# Or in ansible.cfg [defaults] host_key_checking = False

# Or per-host in inventory [target_servers] server-01 ansible_host=10.0.1.50 ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' ```

Step 7: Configure Bastion Host Access

For hosts behind bastion/jump host:

```ini # inventory.ini [private_servers] private-01 ansible_host=10.0.2.50

[private_servers:vars] ansible_ssh_common_args='-o ProxyCommand="ssh -W %h:%p -q bastion.example.com"'

# Or using SSH config [bastion] bastion.example.com

[private_servers] private-01 ansible_host=10.0.2.50 ansible_ssh_common_args='-J bastion.example.com' ```

SSH config file approach:

```bash # ~/.ssh/config Host bastion HostName bastion.example.com User ansible IdentityFile ~/.ssh/ansible_key

Host private-* ProxyJump bastion User ansible IdentityFile ~/.ssh/ansible_key ```

Step 8: Debug Connection Issues

Use Ansible's verbose modes:

```bash # Single verbose ansible all -m ping -v

# More verbose ansible all -m ping -vv

# Maximum verbose ansible all -m ping -vvv

# Debug mode ansible all -m ping -vvvv ```

Use Ansible's ping module with debugging:

```bash # Test connectivity ansible target-host -m ping -vvv

# Test with raw module (no Python required) ansible target-host -m raw -a "hostname" -vvv

# Test SSH arguments ansible target-host -m ping -e "ansible_ssh_common_args='-v'" -vvv ```

Create a connection test playbook:

```yaml # test_connection.yml - name: Test SSH connection hosts: all gather_facts: false connection: ssh

tasks: - name: Test ping ping: register: ping_result

  • name: Show ping result
  • debug:
  • msg: "Ping result: {{ ping_result }}"
  • name: Test command execution
  • command: hostname
  • register: hostname_result
  • name: Show hostname
  • debug:
  • msg: "Connected to: {{ hostname_result.stdout }}"
  • `

Verification

Test SSH connectivity:

```bash # Quick ping test ansible all -m ping

# Expected output: # server-01 | SUCCESS => { # "ansible_facts": { # "discovered_interpreter_python": "/usr/bin/python3" # }, # "changed": false, # "ping": "pong" # }

# Test with all hosts ansible all -m ping -f 10

# Test gather facts ansible all -m setup -a "filter=ansible_hostname"

# Test with playbook ansible-playbook test_connection.yml ```

Verify inventory configuration:

```bash # List hosts in inventory ansible all --list-hosts

# Show host variables ansible all -m debug -a "msg={{ hostvars[inventory_hostname] }}"

# Check ansible_user ansible all -m debug -a "msg={{ ansible_user }}" ```

Run actual playbook:

```bash # Run playbook with verbose output ansible-playbook site.yml -v

# Check no unreachable errors # All hosts should show SUCCESS or FAILED, not UNREACHABLE ```

  • [ansible-ssh-host-key-verification-failed](/articles/ansible-ssh-unreachable-host-key-verification-failed)
  • [ansible-ssh-timeout-issues](/articles/ansible-ssh-timeout-issues)
  • [ansible-bastion-host-configuration](/articles/ansible-bastion-host-configuration)
  • [WordPress troubleshooting: Ansible Artifact Download Uses an Old Mi](ansible-artifact-download-uses-an-old-mirror-after-proxy-change)
  • [WordPress troubleshooting: Ansible Audit Trail Misses Events Under ](ansible-audit-trail-misses-events-under-burst-load)
  • [WordPress troubleshooting: Ansible Background Worker Gets Stuck in ](ansible-background-worker-stuck-in-a-retry-loop)
  • [WordPress troubleshooting: Ansible Backup Completes but Restore Fai](ansible-backup-completes-but-restore-fails-checksum-validation)
  • [WordPress troubleshooting: Ansible Batch Importer Duplicates Rows A](ansible-batch-importer-duplicates-rows-after-a-retry)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "WordPress troubleshooting: Ansible SSH Connection Refused - Host Un", "description": "Learn how to fix Ansible SSH Connection Refused - Host Unreachable. Professional WordPress troubleshooting solutions with step-by-step guidance. WP error fix, WordPress optimization, WP security, WordPress performance.", "url": "https://www.fixwikihub.com/ansible-ssh-connection-refused-unreachable", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2025-12-15T21:05:18.565Z", "dateModified": "2025-12-15T21:05:18.565Z" } </script>