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:
$ 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:
$ 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:
$ 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:
$ 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:
$ 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:
$ 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:
$ ssh target-host
ssh: connect to host target-host port 22: Connection refused2. 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:
$ 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:
$ 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:
$ ping target-host
PING target-host (10.0.2.50): 56 data bytes
Request timeout for icmp_seq 0
# No route or routing issueStep-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:
# 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 ```
Related Issues
- [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)
Related Articles
- [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>