Ansible: ignorar hosts inaccesibles
Aprenda a distinguir hosts inaccesibles de tareas fallidas en Ansible y a usar ignore_unreachable, serial y max_fail_percentage sin perder los omitidos.
Un host inaccesible no indica un error de tarea
Para ignorar los hosts inaccesibles en Ansible, establezca ignore_unreachable: true. Esta opción funciona. Lo importante es saber cuándo usarla, porque Ansible gestiona estos dos problemas de forma diferente. Una tarea que se ejecutó en el host y devolvió un error es un fallo. Un host al que Ansible no pudo conectarse es inaccesible. ignore_errors sólo cubre el primer caso. ignore_unreachable sólo cubre el segundo.
Esta es la diferencia en el resumen de la ejecución.
PLAY RECAP *********************************************************************
web1 : ok=7 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0Ansible se conectó a web1 y ejecutó siete tareas. web2 muestra unreachable=1 y failed=0, lo que significa que no se ejecutó ninguna tarea en ese host. Ansible no logró establecer la conexión, por lo que retiró el host de la ejecución y continuó con el resto. Si esa ejecución instalaba una actualización de seguridad, uno de sus servidores no la tiene.
Qué hace que un host no sea accesible
No accesible significa que la conexión falló antes de que algún módulo llegara al host. No hay salida del módulo que revisar. Sólo aparece un error de conexión en la primera tarea que contacta con la máquina.
fatal: [web2]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.20 port 22: Connection refused", "unreachable": true}El campo msg contiene la causa real. Estos son los casos que encontrará:
Connection refused: se rechazó la conexión TCP, por lo que no hay ningún proceso escuchando en ese puerto.sshdestá detenido, o SSH se trasladó a otro puerto y el inventario todavía indica el 22.Connection timed out: no respondió nada. Un firewall está descartando los paquetes o el servidor está apagado. Cada intento consume todo el tiempo de espera de la conexión, que es de 10 segundos de forma predeterminada.Host key verification failed.: la clave de~/.ssh/known_hostsno coincide con la clave que presentó el servidor. Una VPS reinstalada conserva su dirección IP, pero obtiene una clave de host nueva. Esto es normal después de una reinstalación y grave en cualquier otro momento.Permission denied (publickey): SSH respondió y rechazó su clave. El puerto funciona, por lo que se trata de un problema de autenticación, normalmente unansible_userincorrecto o una clave que no está cargada.Timeout (12s) waiting for privilege escalation prompt: la conexión funcionó, perobecomeno.sudoestá esperando una contraseña que nunca llega.
La falta de un intérprete de Python suele considerarse una causa de esa lista, pero no lo es. SSH se conecta, por lo que el host es accesible. Después, el módulo no tiene nada que ejecutar:
fatal: [db1]: FAILED! => {"changed": false, "module_stdout": "/bin/sh: 1: /usr/bin/python3: not found\r\n", "msg": "The module failed to execute correctly, you probably need to set the interpreter", "rc": 127}Esa línea indica FAILED! y el resumen la cuenta como failed, por lo que ignore_unreachable nunca lo contactará. Configure ansible_python_interpreter para ese host o instale python3 en él.
Cómo ignorar los hosts inaccesibles en un play
En el nivel de tarea, la palabra clave se coloca junto al módulo:
- name: Read the package list, and do not stop if the host is down
ansible.builtin.command: dpkg -l
register: packages
changed_when: false
ignore_unreachable: trueEn el nivel de play, establece el valor predeterminado para todas las tareas del play. Una tarea concreta puede volver a activarlo:
- name: Opportunistic fleet maintenance
hosts: all
ignore_unreachable: true
tasks:
- name: This runs, cannot connect, and the play carries on
ansible.builtin.ping:
- name: This one still ends the play for a host that is down
ansible.builtin.ping:
ignore_unreachable: falseEs importante entender qué cambia internamente. Con ignore_unreachable definido, el host ya no se elimina del play. Por tanto, cada tarea posterior intenta conectarse de nuevo y vuelve a fallar de la misma forma. Cada intento espera a que venza el tiempo de espera de conexión: 10 segundos, a menos que cambie timeout en ansible.cfg. Un play de veinte tareas contra un servidor caído añade unos 200 segundos a la ejecución y veinte líneas rojas al registro.
Por tanto, compruebe una vez la conexión y detenga después ese host de forma limpia:
- name: Opportunistic fleet maintenance
hosts: all
gather_facts: false
tasks:
- name: Check that the host answers before doing any work
ansible.builtin.ping:
register: reachable
ignore_unreachable: true
- name: End the play for this host if it never answered
ansible.builtin.meta: end_host
when: reachable.unreachable | default(false)
- name: Gather facts now that the connection is known good
ansible.builtin.setup:
- name: Refresh the package index
ansible.builtin.apt:
update_cache: true
become: trueAsí se realiza un intento de conexión por cada host caído, en lugar de uno por tarea. end_host, añadido en Ansible 2.8, finaliza el play para el host actual sin marcarlo como fallido. La clave unreachable sólo existe en el resultado registrado cuando falla la conexión. Por eso, default(false) mantiene válida la condición en todos los hosts que respondieron. La recopilación de datos está desactivada en el nivel de play porque, de lo contrario, la tarea implícita Gathering Facts sería la que encontraría la conexión rota, y usted quiere que esa tarea sea su propio ping.
ignore_unreachable es una palabra clave de play y de tarea. Déjela en el playbook, donde el lector pueda verla, en lugar de ocultarla dentro de un role, porque determina qué hosts puede omitir una ejecución. La separación entre playbooks y roles explica qué capa debe encargarse de una configuración de este tipo.
Por qué ignore_errors no es la herramienta adecuada aquí
La documentación de Ansible es clara sobre esta limitación. ignore_errors «sólo funciona cuando la tarea puede ejecutarse y devuelve un valor de "failed". No hace que Ansible ignore errores de variables no definidas, fallos de conexión, problemas de ejecución (por ejemplo, paquetes que faltan) ni errores de sintaxis».
Un fallo de conexión nunca se convierte en un resultado de tarea con failed: true. Llega como una marca independiente, y Ansible procesa esa marca primero: el host se añade a la lista de inalcanzables y sale del play. Aunque añada ignore_errors: true a las doce tareas de un play, un host con el puerto SSH cerrado se detiene en la primera. Esta es la confusión más habitual en este ámbito. Conviene buscarla con grep en sus playbooks antiguos, especialmente en los que escribió mientras aprendía a escribir su primer playbook para un VPS.
Depure antes de suprimir
La supresión permanente provoca que la flota se desvíe de su estado esperado, porque el host al que nadie puede acceder también es el host que nadie actualiza. Siga primero este orden. Todos los comandos de esta sección sólo leen.
ansible web2 -i inventory.ini -m ansible.builtin.ping -oejecuta un módulo contra un host y muestra una línea.- Añada
-vvvval mismo comando. Ansible muestra el comando ssh completo que construye, incluidos el usuario de destino, el puerto, la clave privada y las opciones que pasa. - Ejecute usted mismo ese comando ssh con
-v. Si ssh no puede conectarse, el problema está por debajo de Ansible y ninguna palabra clave del playbook lo resolverá. - Lea la cadena
msgy compárela con la lista anterior.Connection refusedyConnection timed outapuntan a dos lugares distintos: uno al servicio SSH y otro a la ruta de red. - Para
Host key verification failed., compruebe lo que tiene almacenado conssh-keygen -F web2.example.com. Si el servidor se reconstruyó, elimine la entrada antigua conssh-keygen -R web2.example.comy acepte la clave nueva después de comprobarla en la consola del proveedor. Establecerhost_key_checking = Falseenansible.cfgelimina el error y también desactiva la comprobación que permitiría detectar que otra máquina está respondiendo ahora en esa dirección. - Para
Permission denied (publickey), confirme qué cree Ansible que debe usar.ansible-inventory -i inventory.ini --host web2muestra las variables activas, incluidasansible_useryansible_port. - Si SSH funciona pero los módulos no, compruebe el intérprete con
ansible web2 -m ansible.builtin.raw -a 'command -v python3 || echo none'. El módulorawejecuta un comando mediante el shell y no necesita Python en el destino.
Sólo después de esto ignorar el host será una decisión y no un hábito.
El resumen cuenta los hosts inalcanzables por separado, y CI normalmente no detecta ese caso
ansible-playbook sale con 0 si termina correctamente, con 2 cuando al menos un host ha fallado y con 4 cuando al menos un host es inalcanzable. Esos dos valores son indicadores de bits en el código fuente, por lo que una ejecución con un host fallido y otro inalcanzable termina con 6. El comando ansible devuelve los mismos códigos. Estos valores se comprobaron en el código fuente de ansible-core en agosto de 2026.
Ahora establezca ignore_unreachable: true y ejecute las mismas siete tareas contra el mismo host desconectado:
PLAY RECAP *********************************************************************
web1 : ok=7 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=7web2 informa de unreachable=0 y de siete tareas ok, y la ejecución termina con 0. Cuando se establece esta palabra clave, Ansible incrementa los contadores ok y ignored para ese host en lugar del contador que denomina dark, que es el que completa la columna unreachable. Las líneas rojas UNREACHABLE! se siguen mostrando, por lo que el registro es veraz, pero el resumen y el código de salida no lo son.
Un trabajo de CI que ejecuta el playbook y comprueba sólo $? considera correcta esa ejecución. Ningún dato del resumen indica que nunca se haya contactado con una máquina. Haga que la comprobación de accesibilidad sea un paso independiente, antes del play:
ansible all -i inventory.ini -m ansible.builtin.ping -oEsto muestra una línea por host y termina con 4 si algún host es inalcanzable. Así, la canalización tiene una condición con la que fallar y el registro incluye los nombres de los hosts. ping necesita un intérprete de Python operativo en el destino, por lo que comprueba algo más que la conexión. Normalmente, eso es lo que se necesita. Después, ejecute el playbook con ignore_unreachable para que los hosts accesibles reciban el cambio.
any_errors_fatal y max_fail_percentage en un lote
Estas dos palabras clave de play determinan qué ocurre cuando algo falla en una parte del conjunto de hosts. Además, tratan los hosts inaccesibles de forma diferente.
any_errors_fatal: true sí reacciona ante un host inaccesible. Ansible termina la tarea actual en el resto del lote y después detiene el play para todos los hosts incluidos en él. Úselo cuando la ejecución sólo tenga sentido si se completa de forma íntegra, como en un cambio coordinado de esquema.
max_fail_percentage: 30 no reacciona ante un host inaccesible. La comprobación divide el número de hosts que han fallado entre el tamaño del lote. Los hosts inaccesibles se mantienen en una lista independiente, por lo que nunca se incluyen en ese número. Diez hosts, de los cuales cuatro son inaccesibles, siguen ejecutándose con max_fail_percentage: 10. En cambio, si dos hosts fallan una tarea, el play se detiene. La documentación añade otra condición importante: "The percentage set must be exceeded, not equaled." Con serial: 4, para detenerse después de dos fallos en un lote de cuatro hay que escribir 49, no 50.
Hay un caso en el que los hosts inaccesibles detienen la ejecución por sí solos. Si todos los hosts del lote han fallado o son inaccesibles, Ansible ya no tiene ningún host disponible y termina el play con NO MORE HOSTS LEFT.
serie: aplicar un cambio de forma progresiva en toda la flota
- name: Rolling nginx config update
hosts: webservers
serial: 2
max_fail_percentage: 25
tasks:
- name: Deploy the site config
ansible.builtin.template:
src: site.conf.j2
dest: /etc/nginx/conf.d/site.conf
owner: root
mode: "0644"
become: true
notify: Reload nginx
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
become: trueserial: 2 ejecuta todo el play en dos hosts, lo completa y después inicia el siguiente grupo de dos. serial: "25%" se adapta al tamaño del grupo. Una lista, serial: [1, 5, 10], define una estrategia canario: primero un host, después cinco y luego diez; los hosts restantes se ejecutan en grupos del último tamaño utilizado. max_fail_percentage se mide por grupo, por lo que ambos parámetros funcionan conjuntamente. Si se rompe la primera máquina, la ejecución se detiene antes de afectar a cuarenta. Esto permite administrar una flota de servidores Linux desde una máquina de control de forma segura con un solo comando.
Cuándo ignorar los hosts inaccesibles y cuándo no
Ignórelos para trabajos oportunistas. Una ejecución de recopilación de datos o una comprobación horaria de desviaciones no pierde nada al omitir un host que está apagado, porque la siguiente pasada lo recupera. ignore_unreachable: true a nivel de play es la opción adecuada en ese caso, junto con el paso de ping, para que los nombres omitidos aparezcan en un lugar que una persona pueda revisar.
No los ignore durante una ejecución de parches de seguridad. El valor de esa ejecución es garantizar que todos los hosts tengan la corrección, y ocultar el estado inaccesible convierte «un servidor sigue siendo vulnerable» en un resumen final sin errores. El host que lleva dos semanas inaccesible es el que probablemente está más desactualizado. Haga que esa ejecución termine con el código 4 y que una persona lo revise.
En ambos casos se aplica la misma regla: suprima la detención, nunca el registro. Si se omitió un host, algo debe indicarlo: el resumen final, el registro de CI o una alerta de monitorización. Ansible sólo sabe que existe un host durante los segundos en que un play se ejecuta contra él, por lo que no es un buen lugar para saber que un servidor está caído desde el martes. Ese trabajo corresponde a la monitorización, y un playbook de Ansible que instala Zabbix proporciona una visión de toda la flota en una tarde.
FAQ
¿Cuál es la diferencia entre ignore_errors e ignore_unreachable en Ansible?
ignore_errors: true se aplica a una tarea que se ejecutó en el host y devolvió un error, por ejemplo, porque un comando terminó con un código distinto de cero. ignore_unreachable: true se aplica a un host al que Ansible no pudo conectarse y en el que no se ejecutó ningún módulo. Cada opción lee campos distintos del resultado de la tarea y ninguna cubre el caso de la otra. La documentación de Ansible indica que ignore_errors «no hace que Ansible ignore los errores por variables no definidas, los fallos de conexión, los problemas de ejecución (por ejemplo, la ausencia de paquetes) ni los errores de sintaxis», y un puerto SSH cerrado es un fallo de conexión.
¿ignore_unreachable oculta el host en el resumen de ejecución?
En la práctica, sí. Con esta palabra clave configurada, Ansible deja de contar ese host en unreachable y lo cuenta como ok y ignored una vez por tarea; después, la ejecución termina con el código 0. Las líneas de fatal: [host]: UNREACHABLE! siguen apareciendo, por lo que el registro es correcto aunque el resumen y el código de salida no lo sean. Revise la columna ignored o ejecute ansible all -m ansible.builtin.ping -o como un paso independiente, para que un host inalcanzable siga generando un código de salida distinto de cero en algún punto.
¿Qué código de salida devuelve ansible-playbook cuando un host es inalcanzable?
Devuelve 4. Una ejecución con al menos un host con errores devuelve 2. Ambos valores son indicadores de bits, por lo que una ejecución con errores y con un host inalcanzable devuelve 6. Una ejecución correcta devuelve 0. Estos códigos se comprobaron con el código fuente de ansible-core en agosto de 2026. Configurar ignore_unreachable: true elimina el 4. Por eso, una canalización que comprueba sólo el código de salida no puede detectar una máquina omitida.
¿Cómo omito el resto de un play para un host que nunca respondió?
Haga que la primera tarea sea ansible.builtin.ping con ignore_unreachable: true y register: reachable. Después, añada ansible.builtin.meta: end_host con la condición when: reachable.unreachable | default(false). end_host termina el play para ese host sin marcarlo como fallido. Configure gather_facts: false en el play, para que la prueba de conexión sea la tarea que detecte el fallo. Sin este patrón, el host inactivo permanece en el play y cada tarea posterior vuelve a esperar el tiempo de espera de la conexión.
¿Debo ignorar los hosts inalcanzables durante una ejecución de aplicación de parches de seguridad?
No. Una ejecución de parches es útil porque garantiza que todos los hosts tienen la actualización. Ignorar los hosts inalcanzables sustituye esa garantía por un resumen correcto en apariencia. Deje que la ejecución termine con el código 4, lea los nombres de los hosts que no respondieron y corríjalos. La supresión sólo corresponde a ejecuciones oportunistas repetidas, en las que la siguiente pasada detectará lo que no se haya aplicado.