Limitations of Azure DevOps Build Timeout for Background Tasks
0 reputation · 29 May 2026, 08:56 UTC
0 reputation · 29 May 2026, 08:56 UTC
We want a pipeline job to terminate all running processes, including those started by a step, when the job exceeds its timeout or is canceled via the REST API.
The agent wrapper only tracks the main process started for a job. Child processes launched by a task (e.g., a long‑running background service) are not monitored, so they may continue to run after the job is marked as timed out or canceled. Azure DevOps does not expose an API or YAML directive to cancel individual steps or to signal child processes.
33775 reputation · 29 May 2026, 12:38 UTC
1. Built-in mechanism to detect/kill orphaned child processes? No. The agent only tracks the direct child process it launches for each step. When a job times out or is canceled via the REST API, the agent sends SIGTERM (Linux/macOS) or CTRL_BREAK_EVENT (Windows) to that process group. Any process that has detached — via double-fork, setsid, nohup, Docker -d, or Windows CREATE_BREAKAWAY_FROM_JOB — becomes orphaned and survives.
2. Can the agent forward the cancellation signal to child processes? Only if the intermediate process (the script or wrapper you invoke) does not ignore SIGTERM and does not create a new process group. Most background services (daemonized Java, Node, docker run -d) explicitly detach, breaking the chain. There is no YAML directive, task property, or API to enumerate or kill individual steps or their descendants.
3. Best-practice patterns for fine-grained cancellation? Use one of the following patterns, chosen based on agent type and process architecture.
jobCancelTimeout (default 5 min) controls how long the agent waits after SIGTERM before SIGKILL; increasing it requires a self-hosted agent (_AGENT_JOB_CANCEL_TIMEOUT env var). Microsoft-hosted agents ignore this setting.Agent.JobCancelToken file (since agent v2.190+) appears after the cancel signal; tasks must poll it or register a handler — no synchronous callback exists.# start-service.sh
trap 'kill -TERM -$child_pgid 2>/dev/null; wait $child_pgid 2>/dev/null' TERM
set -m # enable job control so the script becomes group leader
./actual-service &
child_pgid=$!
wait $child_pgid
Call this script directly from the pipeline step (no extra shell). The script stays in the same process group as the agent's child, receives SIGTERM, and forwards to its process group (-$child_pgid).
timeout with foreground mode (Linux/macOS agents)- script: |
timeout --foreground --kill-after=30s 10m ./start-service.sh
displayName: Run service with hard timeout
--foreground keeps the timeout command as the group leader so it propagates signals.
finally: step with PID tracking (self-hosted agents, deeper cleanup)steps:
- script: |
./start-service.sh &
echo $! > service.pid
name: start
- script: ...
# main work
- task: Bash@3
condition: always()
inputs:
targetType: inline
script: |
if [[ -f service.pid ]]; then
kill -TERM -$(cat service.pid) 2>/dev/null
sleep 5
kill -KILL -$(cat service.pid) 2>/dev/null
fi
displayName: Ensure service cleanup
Records the PID at launch, then kills the entire process group in a finally-style step (condition: always()).
- script: docker run --rm --init my-image
# or
- script: docker compose up --abort-on-container-exit
Running in foreground keeps the container in the agent's process tree; --init provides a proper init process to reap zombies.
Agent.JobCancelToken (advanced)Write a Node/PowerShell task that watches the cancel-token file and forwards SIGTERM to recorded child PIDs. This gives synchronous-ish handling but requires custom task development.
timeoutInMinutes: 1 that starts a background sleep 300 & and captures $!. Check the agent log — the background sleep will survive the timeout.finally: step with pkill -TERM -P $$; sleep 2; pkill -KILL -P $$ and verify the background sleep is reaped on a Microsoft-hosted Ubuntu agent.Start-Job { sleep 300 } and cancel via REST API; check Get-Job state after jobCancelTimeout.Are you running on Microsoft-hosted or self-hosted agents, and is the background process a Docker container, a daemonized service (Java/Node), or a shell script you control? The answer determines which pattern (A–E) is viable and whether jobCancelTimeout tuning is possible.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.