Enhanced GitHub Actions runner container hooks for Kubernetes and Docker environments. This fork improves error reporting and diagnostics for pod lifecycles to help troubleshoot issues at runtime.
This fork extends the original actions/runner-container-hooks with enhanced error reporting and diagnostics for pod lifecycles in Kubernetes environments. When pod scheduling or execution fails, you get actionable error messages instead of cryptic Kubernetes API errors.
Before (Original Hook):
Error: Pod failed to come online with error: pods "runner-xyz" was not ready
After (Enhanced Diagnostics):
Error: Pod scheduling failed due to CPU resource constraints: pods "runner-xyz" was not ready
== Pod Events ==
[2026-04-11T10:15:30.000Z] Warning FailedScheduling: 0/3 nodes available: 2 Insufficient cpu, 1 node(s) had taint {node-role: infra}, that the pod didn't tolerate.
Container Statuses:
- runner-xyz: Not Ready
Waiting: CircuitBreaking, node(s) had insufficient resources
CPU resource constraint detected. The job requires more CPU than available on the node.
Consider reducing CPU requests in your workflow or using a node with more resources.
Key Improvements:
- Detailed pod failure diagnostics with container logs, events, and status conditions
- Resource constraint detection (CPU/memory) with actionable error messages
- Node selector and affinity issue reporting
- Immediate pod status checking after creation for early failure detection
- Comprehensive logging of pod manifests and container resources
- Redacted sensitive values in environment variables from logs
Pre-built images available on GitHub Container Registry:
ghcr.io/echohello-dev/runner-container-hooks:latest
Multi-platform: linux/amd64, linux/arm64
The cleanest approach - uses an init container to copy hooks from the published image to a volume mount. No need to modify your runner image.
apiVersion: actions.github.com/v1alpha1kind: AutoscalingRunnerSetmetadata:
name: actions-runner-setnamespace: actions-runnerspec:
runnerScaleSetName: actions-runner-setmaxRunners: 10minRunners: 0template:
spec:
initContainers:
- name: install-hooksimage: ghcr.io/echohello-dev/runner-container-hooks:latestcommand: ['sh', '-c', 'cp -r /home/runner/k8s/* /hooks/']volumeMounts:
- name: runner-hooksmountPath: /hookscontainers:
- name: runnerimage: ghcr.io/actions/actions-runner:latestcommand: ['/home/runner/run.sh']env:
- name: ACTIONS_RUNNER_CONTAINER_HOOKSvalue: /hooks/index.js
- name: ACTIONS_RUNNER_POD_NAMEvalueFrom:
fieldRef:
fieldPath: metadata.name
- name: ACTIONS_RUNNER_REQUIRE_JOB_CONTAINERvalue: "true"volumeMounts:
- name: runner-hooksmountPath: /hooks
- name: workmountPath: /home/runner/_workvolumes:
- name: runner-hooksemptyDir: {}
- name: workephemeral:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]resources:
requests:
storage: 10GiFor development or when you want to quickly test changes without rebuilding images:
apiVersion: v1kind: ConfigMapmetadata:
name: runner-container-hooksnamespace: actions-runnerdata:
index.js: | module.exports = require('./lib/src/index.js')lib.src.index.js: | // hook content herelib.src.hooks.prepare-job.js: | // hook content here---
apiVersion: actions.github.com/v1alpha1kind: AutoscalingRunnerSet# ... rest of config with volumes referencing ConfigMapIf you prefer to bake the hooks into your runner image:
# DockerfileFROM ghcr.io/actions/actions-runner:latest
# Copy k8s container hooksCOPY --from=ghcr.io/echohello-dev/runner-container-hooks:latest /home/runner/k8s /home/runner/k8s
# Or copy docker hooksCOPY --from=ghcr.io/echohello-dev/runner-container-hooks:latest /home/runner/docker /home/runner/docker
ENV ACTIONS_RUNNER_CONTAINER_HOOKS=/home/runner/k8s/index.jsFor testing or temporary configurations:
initContainers:
- name: download-hooksimage: curlimages/curl:latestcommand: ['sh', '-c', 'curl -sfL https://github.com/echohello-dev/runner-container-hooks/releases/latest/download/k8s-index.js -o /hooks/index.js && chmod +x /hooks/index.js']volumeMounts:
- name: runner-hooksmountPath: /hooksThe modern GitHub-managed scaling API:
apiVersion: actions.github.com/v1alpha1kind: AutoscalingRunnerSetmetadata:
name: actions-runner-setnamespace: actions-runnerspec:
runnerScaleSetName: actions-runner-setmaxRunners: 10minRunners: 0template:
spec:
initContainers:
- name: install-hooksimage: ghcr.io/echohello-dev/runner-container-hooks:latestcommand: ['sh', '-c', 'cp -r /home/runner/k8s/* /hooks/']volumeMounts:
- name: runner-hooksmountPath: /hookscontainers:
- name: runnerimage: ghcr.io/actions/actions-runner:latestcommand: ['/home/runner/run.sh']env:
- name: ACTIONS_RUNNER_CONTAINER_HOOKSvalue: /hooks/index.js
- name: ACTIONS_RUNNER_POD_NAMEvalueFrom:
fieldRef:
fieldPath: metadata.name
- name: ACTIONS_RUNNER_REQUIRE_JOB_CONTAINERvalue: "true"volumeMounts:
- name: runner-hooksmountPath: /hooks
- name: workmountPath: /home/runner/_workvolumes:
- name: runner-hooksemptyDir: {}
- name: workephemeral:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]resources:
requests:
storage: 10GiFor organizations still using the community-maintained ARC:
apiVersion: actions.summerwind.dev/v1alpha1kind: RunnerSetmetadata:
name: actions-runner-setnamespace: actions-runnerspec:
replicas: 2runnerManagementURL: https://github.com/YOUR_ORGgithubToken:
secretName: github-tokentemplate:
spec:
repository: YOUR_REPOcontainerMode:
type: dindcontainerHooks:
path: /hooks/index.jsimage: ghcr.io/actions/actions-runner:latestimagePullPolicy: AlwaysinitContainers:
- name: install-hooksimage: ghcr.io/echohello-dev/runner-container-hooks:latestcommand: ['sh', '-c', 'cp -r /home/runner/k8s/* /hooks/']volumeMounts:
- name: runner-hooksmountPath: /hooksenv:
- name: ACTIONS_RUNNER_POD_NAMEvalueFrom:
fieldRef:
fieldPath: metadata.name
- name: ACTIONS_RUNNER_REQUIRE_JOB_CONTAINERvalue: "true"volumeMounts:
- name: runner-hooksmountPath: /hooksvolumes:
- name: runner-hooksemptyDir: {}| Package | Description |
|---|---|
packages/k8s | Kubernetes hook implementation for ARC |
packages/docker | Docker-based container hooks |
packages/hooklib | Shared TypeScript utilities and interfaces |
runner-container-hooks/
├── packages/
│ ├── k8s/ # Kubernetes hook implementation
│ │ ├── src/
│ │ │ ├── hooks/ # Hook implementations
│ │ │ └── k8s/ # Kubernetes utilities
│ │ ├── tests/ # Unit and integration tests
│ │ └── entrypoint.js # Entry point for ARC
│ ├── docker/ # Docker hook implementation
│ └── hooklib/ # Shared library
├── examples/ # Example configurations
├── .github/workflows/ # CI/CD workflows
└── mise.toml # Task definitions
- Node.js 20+
- Docker (for local development and testing)
- Kind (for Kubernetes testing)
- mise (for task running)
mise run installmise run buildmise run testNote: Tests require a Kind cluster and Docker to be running.
mise run install # Install dependencies
mise run build # Build all packages
mise run test# Run tests (requires Kind + Docker)
mise run lint # Run linting
mise run format # Format code
mise run create:kind # Create Kind test cluster
mise run delete:kind # Delete Kind test clusterMIT