Fix Ansible 'Unreachable' Host Errors
The Error: Host Unreachable
Your Ansible playbook fails with:
fatal: [web-server-01]: 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
}
Or with a different variant:
fatal: [web-server-01]: UNREACHABLE! => {
"msg": "Failed to connect to the host via ssh:
Permission denied (publickey,gssapi-keyex,gssapi-with-mic)"
}
Ansible cannot establish an SSH connection to the target host.
Root Cause
The UNREACHABLE error is a transport-layer failure, not an Ansible module failure. It means SSH itself cannot connect. Common causes:
- Host is down or unreachable — wrong IP, firewall blocking port 22, or host is offline
- SSH key authentication fails — wrong key, key not added to authorized_keys, or agent not running
- Wrong username — Ansible is connecting as the wrong user
- SSH host key changed — the host was rebuilt and the old key is in
known_hosts - Python missing on the target — Ansible needs Python on the remote host to execute modules
Step-by-Step Fix
1. Test raw SSH connectivity
# Test from the Ansible control node
ssh -v -i ~/.ssh/id_rsa [email protected]
# Test with the exact settings Ansible would use
ansible web-server-01 -m ping -vvvv
The -vvvv flag shows the full SSH command Ansible constructs, including which key, user, and port it uses.
2. Verify host is reachable on the network
# Check basic connectivity
ping -c 3 10.0.1.50
# Check if SSH port is open
nc -zv 10.0.1.50 22
# or
nmap -p 22 10.0.1.50
If the port is filtered, check firewall rules on the target:
# On the target (if accessible via console)
sudo iptables -L -n | grep 22
sudo ufw status
sudo firewall-cmd --list-all
3. Fix SSH key authentication
# Verify the key file exists and has correct permissions
ls -la ~/.ssh/id_rsa
# Must be -rw------- (600)
chmod 600 ~/.ssh/id_rsa
# Ensure the public key is on the target
ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]
# Check the SSH agent has the key loaded
ssh-add -l
ssh-add ~/.ssh/id_rsa
4. Check your Ansible inventory and variables
# Show how Ansible resolves connection vars for a host
ansible-inventory --host web-server-01
Verify these variables in your inventory:
[webservers]
web-server-01 ansible_host=10.0.1.50 ansible_user=deploy ansible_ssh_private_key_file=~/.ssh/id_rsa
Or in group_vars/all.yml:
ansible_user: deploy
ansible_ssh_private_key_file: ~/.ssh/id_rsa
ansible_port: 22
5. Fix host key verification failures
If the target was rebuilt and has a new host key:
# Remove the old key
ssh-keygen -R 10.0.1.50
# Or for a hostname
ssh-keygen -R web-server-01
To disable host key checking (only in dev/CI):
# ansible.cfg
[defaults]
host_key_checking = False
6. Fix missing Python on the target
fatal: [web-server-01]: FAILED! => {
"msg": "/usr/bin/python: No such file or directory"
}
Set the Python interpreter explicitly:
# Inventory
web-server-01 ansible_python_interpreter=/usr/bin/python3
Or use the raw module to install Python first:
- name: Bootstrap Python on fresh hosts
hosts: new_servers
gather_facts: false
tasks:
- name: Install Python
raw: apt-get update && apt-get install -y python3
become: true
7. Increase connection timeout
For slow networks or cloud instances that take time to boot:
# ansible.cfg
[defaults]
timeout = 30
[ssh_connection]
ssh_args = -o ConnectTimeout=30 -o ConnectionAttempts=3
Prevention Tips
- Use
ansible -m pingas a smoke test before running playbooks to catch connectivity issues early. - Standardize SSH configuration across all managed hosts — same user, same key, same Python path.
- Set
ansible_python_interpreter = autoin modern Ansible (2.12+) to auto-detect Python. - Use SSH multiplexing in
ansible.cfgfor faster connections:[ssh_connection] pipelining = True ssh_args = -o ControlMaster=auto -o ControlPersist=60s - Test inventory changes in a dry run (
--check) before applying playbooks to production.
Was this article helpful?
SRE & Observability Engineer
If it's not measured, it doesn't exist. SLO-driven, metrics-obsessed, and the person who gets paged at 3 AM so you don't have to. Observability isn't optional.
Related Articles
Fix Ansible 'Permission Denied' on Become (Sudo)
Resolve Ansible become/sudo permission denied errors — fix sudoers config, passwordless sudo, and become method settings.
Fix SSH 'Connection Refused' After Server Reboot
Diagnose and fix SSH connection refused errors after a Linux server reboot, covering sshd service, firewall rules, port configuration, and host key issues.
Ansible Custom Filters: Transforming Data In Jinja2 Templates
If you've spent any serious time with Ansible, you've probably hit the wall where the built-in Jinja2 filters just don't cut it anymore. You're wrestling w...
Ansible Handlers: Triggering Service Restarts Only When Configuration Changes
If you've spent any time managing infrastructure with Ansible, you've probably run into this scenario: you're deploying a configuration change to nginx acr...
Ansible Tags: Selective Task Execution For Faster Deployments
If you've ever run a full Ansible playbook just to push a single config change and watched it grind through 47 tasks before touching the one you care about...
Ansible Dynamic Inventory: Automating Cloud Infrastructure
Use dynamic inventories to automatically discover and manage cloud infrastructure — AWS EC2, Azure VMs, and GCP instances with Ansible inventory plugins.
More in Ansible
View all →Ansible Fundamentals: Your First Playbook to Production
Learn Ansible from scratch — install Ansible, write your first playbook, understand modules, manage inventories, and automate server configuration.
Ansible Roles and Galaxy: Structuring Automation at Scale
Structure your Ansible automation with roles for reusability. Learn role directory structure, Galaxy usage, dependencies, and testing with Molecule.
Ansible Vault: Encrypting Secrets in Your Automation
Encrypt sensitive data with Ansible Vault — encrypt files, inline variables, multi-password setups, and integrate with external secret managers like HashiCorp Vault.