From 6f236a4ab415981f4fcc8e3d5657619eaaf80bfa Mon Sep 17 00:00:00 2001 From: Jason Ernst Date: Mon, 10 Aug 2026 12:05:22 -0700 Subject: [PATCH] fix: bound container stop and raise Docker API timeout on deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replacing an existing runner container (image bump) stops and removes it first. A crash-looping or wedged runner can take longer than the Docker SDK's 60s default read timeout to stop+remove, failing the first playbook run with 'UnixHTTPConnectionPool ... Read timed out (read timeout=60)' — the daemon finishes the removal in the background, which is why an immediate rerun succeeds. Add github_runner_stop_timeout (default 10) to bound the graceful stop and github_runner_docker_timeout (default 180) to give the API client room to wait out a slow removal. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 8 ++++++++ README.md | 2 ++ galaxy.yml | 2 +- roles/github_runner/defaults/main.yml | 9 +++++++++ roles/github_runner/tasks/main.yml | 8 ++++++++ 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58fb408..12aaabf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.1.4] - 2026-08-10 +### Fixed +- First playbook run no longer fails with `UnixHTTPConnectionPool ... Read timed out (read timeout=60)` when replacing an existing runner container. Stopping and removing a crash-looping or wedged runner can exceed the Docker SDK's 60s default client timeout; the daemon finished the removal in the background, which is why an immediate rerun succeeded. + +### Added +- `github_runner_docker_timeout` (default `180`): Docker API client timeout for the deploy task. +- `github_runner_stop_timeout` (default `10`): seconds docker waits after SIGTERM before SIGKILL when stopping the runner container. + ## [0.1.3] - 2026-05-27 ### Added - Added `github_runner_network_mode` variable (defaults to `default`) to control the runner container's Docker network mode. Set to `host` so the in-container `adb` client can reach an adb server running on the host at `127.0.0.1:5037`. diff --git a/README.md b/README.md index 4293682..550b4b6 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ github_runner_env_file | whether to use an env file for pas github_runner_env_filename | the filename of the env file for passing extra environment variables into the container (defaults to ".env") github_runner_github_host | The GITHUB_HOST used for registering the runner (defaults to "github.com") github_runner_persist_config | whether to persist runner configuration across container restarts using a named volume (defaults to true) +github_runner_stop_timeout | seconds docker waits after SIGTERM before SIGKILL when stopping the runner container, e.g. while replacing it on an image bump (defaults to 10) +github_runner_docker_timeout | Docker API client timeout in seconds for the deploy task; stopping and removing a crash-looping runner can exceed the SDK's 60s default (defaults to 180) Notes: the env file lets you do things like set site-specific credentials into the runner that can be built into the code at build time, for instance, Wi-Fi credentials that can be built into tests that are specific to the location of diff --git a/galaxy.yml b/galaxy.yml index 2350f8b..7ad9e66 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -8,7 +8,7 @@ description: "Installs github self hosted repo or org runners within a docker co can be a vanilla runner, or one with java / android installed." license_file: LICENSE readme: README.md -version: 0.1.3 +version: 0.1.4 repository: https://github.com/compscidr/ansible-github-runner tags: - github diff --git a/roles/github_runner/defaults/main.yml b/roles/github_runner/defaults/main.yml index 6d784dc..c0c2a6d 100644 --- a/roles/github_runner/defaults/main.yml +++ b/roles/github_runner/defaults/main.yml @@ -26,3 +26,12 @@ github_runner_network_mode: default github_runner_github_host: "github.com" github_runner_persist_config: true + +# Seconds docker waits after SIGTERM before SIGKILL when stopping the runner +# container (e.g. while replacing it on an image bump). +github_runner_stop_timeout: 10 + +# Docker API client timeout for the deploy task. Stopping and removing a +# crash-looping or wedged runner container can exceed the SDK's 60s default, +# which fails the first playbook run and succeeds on the rerun. +github_runner_docker_timeout: 180 diff --git a/roles/github_runner/tasks/main.yml b/roles/github_runner/tasks/main.yml index d81bf0d..85a0fec 100644 --- a/roles/github_runner/tasks/main.yml +++ b/roles/github_runner/tasks/main.yml @@ -10,6 +10,14 @@ device_cgroup_rules: "{{ ['c 189:* rmw'] if (github_runner_java and github_runner_java_mount_usb) else omit }}" volumes: "{{ runner_volumes }}" restart_policy: unless-stopped + # Replacing an existing container (image bump) stops and removes it first. + # A crash-looping or wedged runner can take well over the Docker SDK's + # 60s default read timeout to stop+remove, which fails the first run and + # then succeeds on the rerun (the daemon finishes the removal in the + # background). Bound the graceful stop and give the API client enough + # room to wait out a slow removal. + stop_timeout: "{{ github_runner_stop_timeout }}" + timeout: "{{ github_runner_docker_timeout }}" network_mode: "{{ github_runner_network_mode }}" ports: "{{ runner_ports }}" env: "{{ runner_env }}"