Stop your Ansible playbooks from stalling on unkillable CI tasks
Technical Briefing | 7/20/2026
You trigger a massive deployment playbook, walk away to grab coffee, and come back to a terminal that hasn’t budged in twenty minutes. The runner isn’t crashed, the network is fine, but the task is sitting there in a D-state or waiting on a pipe that will never open. This has bitten me more times than I care to admit, especially with tasks that involve remote shell execution or long-running apt updates.
The trap of the infinite wait
Ansible defaults are built for interactive use, not necessarily for headless CI environments that need to fail fast. If your remote command hits a prompt you didn’t anticipate or gets stuck on a kernel lock, Ansible just waits forever. You need to enforce timeouts at the task level rather than letting the whole job time out after an hour.
ansible_command: echo 'do work' && sleep 1000
register: my_task
async: 600
poll: 0
- The async keyword tells Ansible to fire and forget the process
- Setting poll to 0 stops Ansible from spamming the connection to check status
- You must follow up with async_status to verify the result before proceeding
When things go sideways
Even with async, you might end up with ghost processes if the runner agent gets killed. Don’t rely on the runner to clean up. I usually keep a cleanup task in the playbook that kills matching patterns on the target node if the previous step failed. If you don’t handle these orphaned processes, you’ll eventually run out of memory or file descriptors on your nodes, and that is a much harder bug to track down.
If you are running these in a pipeline, check the runner’s own timeout configuration. Sometimes it’s the CI agent itself holding the lock on the socket, and no amount of Ansible-side configuration will save you. Keep your timeouts tight and your cleanup tasks explicit.
