GitHub Actions runners
Use a managed runner pool to run jobs from one private GitHub.com repository in your Kube-DC project. GitHub keeps the job queue, logs, and artifacts. Kube-DC runs each accepted job in a temporary worker pod and stores the runner registration credential in your project's OpenBao secret store.
The published github-runner-preview plan has one concurrent worker slot.
Every Cloud project can find the offer in the catalog. If its CPU or memory
quota cannot host the minimum worker and builder, the card shows the
requirements and a support path; creation becomes available after the project
qualifies.
There is no additional runner service, management, or per-job charge. Your
existing Kube-DC resource terms apply to the compute and other resources you
consume. Pool reservations control available capacity; they are not a separate
runner charge. Shared ARC control-plane overhead has no separate runner fee.
Kube-DC support handles this preview under your existing account support terms;
it adds no separate SLA or response-time promise. If creation or a job remains stuck, use your account's support
channel or email support@kube-dc.com. Include
the Project and runner name, approximate time, and sanitized status or events;
never send the GitHub token.
New pools include a temporary Buildx builder for Dockerfile builds. Shell jobs
also run on that pool. Docker Compose, Docker daemon commands, job/service
containers, persistent workspaces, and private worker images are unavailable.
The pool does not supply a Kubernetes or application credential to jobs.
Before you begin
-
Use a private GitHub.com repository whose workflow contributors you trust. In Settings → Actions → General, keep Run workflows from fork pull requests disabled. Do not enable forwarding write tokens or secrets to fork workflows. Treat
pull_request_targetand workflows that check out untrusted code as privileged: this preview is for trusted workflows only. -
Create a short-lived fine-grained GitHub personal access token restricted to that repository. Grant Repository permissions → Administration: Read and write. An organization might require approval before the token works. See GitHub's ARC authentication guide.
-
Have permission to create managed services and to write/read the selected project secret. Authorized project Secret readers and the shared ARC controller can use the registration token. It never belongs in a workflow or Git commit.
-
Check project and organization quota. A Buildx pool reserves an active worker and a possible replacement before it accepts jobs. The pool reservation also includes listener headroom, which runs outside your Organization quota:
Worker size Pool reservation Organization worker requests Organization worker limits Worker Pod slots 1 vCPU / 2 GiB 3.2 vCPU / 6.25 GiB 3 vCPU / 6 GiB 6 vCPU / 8 GiB 2 500m CPU / 1 GiB 2.2 vCPU / 4.25 GiB 2 vCPU / 4 GiB 5 vCPU / 6 GiB 2 Other workloads and idle runner pools also use Organization headroom. If there is not enough room, the service is rejected before placement with
OrganizationQuotaExceeded. Reduce worker size or ask Kube-DC support to review your quota. A later workload can still consume free quota before a worker starts; in that case, the pool reports Capacity unavailable. -
Creation also checks whether a recently observed Linux amd64 node can fit one worker, its builder, and listener headroom. A default 1 vCPU / 2 GiB Buildx pool needs at least 1.7 vCPU / 3.25 GiB free on one eligible node; a 500m CPU / 1 GiB pool needs 1.2 vCPU / 2.25 GiB. Free resources spread across several nodes do not satisfy this check. Idle pools keep capacity holds until deletion releases their reservation. This admission check does not guarantee future scheduling or simultaneous replacement workers.
Create a pool in the console
- Open your project, then select Managed services → New service → GitHub Actions runners.
- Choose the worker CPU and memory in Size. The summary shows the reservation. Buildx is enabled for new pools; use Advanced → Runner execution only if you need a shell-only pool. You can also select a custom worker image by digest. These choices are fixed at creation.
- In Name & access, enter the service name and private repository owner/name. Select a same-project ManagedSecret or enter a token to save in OpenBao. If you just created the Project, wait for its Secrets status to become Ready before saving the token. If the first value write returns an OpenBao 403 while the Project role is enrolling, retry the write to the same ManagedSecret; you do not need to create another secret.
- Review the configuration and create the service. When it reports Ready, open Connect and copy the exact workflow target.
- Use the Run a job example in a trusted branch. Confirm the job succeeds in GitHub and the worker count returns to zero afterward.
If the runner appears as Unavailable in the engine catalog, this Project has not passed runner admission. Review its quota and network/secret prerequisites, then ask Kube-DC support to help qualify it. The card does not permit creating a pool until admission succeeds.
Zero worker pods while idle is expected. During a job, Metrics shows the running worker count and current CPU/memory samples; it keeps recent history after the pod exits. It does not report GitHub queue length or job result.
Build and push a Dockerfile
Use Connect → Build & push after the pool reports Buildx enabled. The following workflow uses the job's GitHub token for GHCR; replace the target, image name, and tag with your own values:
name: Build and push image
on: workflow_dispatch
permissions:
contents: read
packages: write
jobs:
image:
runs-on: RUNNER_TARGET
steps:
- uses: actions/checkout@v7
- name: Sign in to GHCR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: echo "$GH_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin
- run: docker buildx build --push -t ghcr.io/OWNER/IMAGE:TAG .
RUNNER_TARGET is the exact value in Connect. OWNER/IMAGE:TAG names a
package that this repository's GITHUB_TOKEN may publish. If package access
is denied, check repository/package permissions in GitHub. The pool's OpenBao
registration token does not authenticate image pushes. For a validation build
without registry publication, export an OCI archive with Buildx instead.
The builder is selected when the worker starts. Do not add a Docker daemon or
docker-container setup step. Use one docker buildx build --push command in
place of separate docker build and docker push commands. Docker Compose
and services: in workflow jobs require a different runtime and are not
supported by this plan.
Create the same pool with custom resources
Declarative creation uses the same plan, credential permissions, and project boundary. Your GitOps identity needs permission to create the service and read both the named ManagedSecret and its synchronized Kubernetes Secret. Keep the token value outside Git. Apply this ManagedSecret after replacing the namespace:
apiVersion: security.kube-dc.com/v1alpha1
kind: ManagedSecret
metadata:
name: github-runner-auth
namespace: PROJECT_NAMESPACE
spec:
type: opaque
sync:
enabled: true
targetSecretName: github-runner-auth
refreshInterval: 1m
keys: [github_token]
rotation:
enabled: false
Write the token to OpenBao from a mode-0600 file outside Git. The file must
contain only the token: a trailing newline or spaces make it invalid. The CLI
uses your authenticated project access; replace PROJECT_NAMESPACE and
TOKEN_FILE:
kube-dc secrets put github-runner-auth --namespace PROJECT_NAMESPACE \
--from-file=github_token=TOKEN_FILE
If the console backend is unavailable, you can write through an already
authorized OpenBao client. Its policy must permit this secret's path in your
Project KV mount, not other Projects. Configure BAO_ADDR with your verified
OpenBao URL and BAO_NAMESPACE with your Organization name, then authenticate
that client. For a Project named dev, replace PROJECT_NAMESPACE and
TOKEN_FILE below:
version=$(bao kv put -format=json -mount=kv-dev github-runner-auth \
github_token=- < TOKEN_FILE | jq -r '.data.version')
kubectl annotate managedsecret github-runner-auth -n PROJECT_NAMESPACE \
"security.kube-dc.com/value-version=$version" --overwrite
The token travels through standard input, outside Git and shell history. The
annotation starts secret synchronization promptly; without it, the one-minute
refresh interval still applies. This path needs a pre-authorized OpenBao client
and Project-scoped Kubernetes access. Ask Kube-DC support for a narrowly scoped
automation role if you do not already have one. The regular kube-dc secrets put command above remains the simpler path when the backend is available.
Apply the service after replacing PROJECT_NAMESPACE, OWNER, and
REPOSITORY. Omitting parameters.buildMode selects Buildx for a newly created
pool; set it to none explicitly for a shell-only pool:
apiVersion: services.kube-dc.com/v1alpha1
kind: ManagedService
metadata:
name: github-ci
namespace: PROJECT_NAMESPACE
spec:
classRef: {name: github-runner}
planRef: {name: github-runner-preview}
engineVersion: 2.337.0
compute: {cpu: "1", memory: 2Gi}
storage: {mode: None, size: "0"}
topology: {instances: 1}
placement: {mode: ProviderShared}
connectivity: {classRef: {name: tenant-native}}
deletionPolicy: Delete
deletionProtection: true
credentialInputs:
- purpose: github-auth
managedSecretRef: {name: github-runner-auth}
secretRef: {name: github-runner-auth}
authorizationRevision: "1"
github: {owner: OWNER, repository: REPOSITORY}
After applying, wait for Ready and read the exact target:
kubectl wait managedservice/github-ci -n PROJECT_NAMESPACE \
--for=condition=Ready --timeout=10m
kubectl get managedservice github-ci -n PROJECT_NAMESPACE \
-o jsonpath='{.status.engineDetails.workflowTarget}'
You can reapply the same manifests without creating another pool. If the
credential sync is still pending, the service waits. Rotate the token by
writing a new OpenBao version under the same ManagedSecret. Replacing the
ManagedSecret object itself requires a new authorizationRevision and fresh
permission checks. The console shows the same service when it is available.
Customize workers and migrate an existing pool
The managed worker includes Git, Python 3 with PyYAML, Docker CLI, and Buildx. GitHub JavaScript actions run on Node 24. The runner's .NET runtime is for the runner itself; it does not include an application .NET SDK. Node.js application tools and npm are not bundled. Install the application runtime with a setup action in your workflow, or include it in your custom worker image.
Use docker --version and docker buildx inspect --bootstrap to check the
available build tools. docker version contacts a Docker daemon, which this
service does not provide.
To extend the managed worker, use this qualified base image:
FROM docker.io/shalb/kube-dc-github-runner@sha256:e71d15ad6cfe1a83b09da3ff245dcc74878323dabe034b7d66b64bddfd704ab0
USER root
# Install your build tools here, then restore the required worker user.
USER 1001:1001
Publish your custom image, scan its dependencies, and select its immutable digest when creating the pool. You maintain the tools added to your image.
Select 500m–4 CPU and 1–8 GiB of memory for each worker at creation. A custom
worker image must be a public ghcr.io or docker.io Linux amd64 image named
by sha256 digest. It must run as UID/GID 1001 and provide the ARC runner
entrypoint/workspace. A Buildx pool's image must also contain Docker CLI and
the Buildx plugin. Build tools into the image; workflow steps run without sudo.
Worker workspace is temporary, capped at 2 GiB. The worker container has a
4 GiB ephemeral-storage limit. The Buildx builder has a separate 8 GiB
temporary state volume and 10 GiB ephemeral-storage limit. Worker CPU/memory
sizing does not raise those disk limits. If a Dockerfile build exceeds them,
reduce its temporary layers or contact support. Each new worker Pod has a
shared limit of 4,096 processes and threads across the worker and its Buildx
builder. Worker CPU/memory sizing does not raise this limit. If a tool cannot
start more processes or threads, reduce its parallelism or contact Kube-DC
support. Jobs have a 60-minute lifetime.
buildMode, image, and size are fixed at creation. To move an existing
shell-only pool to Buildx, let active jobs finish, delete the old pool with
normal draining, retain its OpenBao ManagedSecret, and create a Buildx pool.
Copy the new runs-on target into affected workflows and run a test job before
resuming regular builds. The preview plan permits one pool per Project; a
replacement can be created only after the old service finishes deleting.
If the replacement fails, create a shell-only pool again and use its newly
reported target, or temporarily route the workflow to another runner you control.
Troubleshoot and delete
| Symptom | Check |
|---|---|
| Service waits for a credential | Open Secrets. If the source is synced, check the token's repository access. If it is not synced, check its status and events before changing the token. |
Secret sync reports x509 or certificate verification errors | Contact Kube-DC support with the Project, secret name, and a sanitized event. The OpenBao trust connection needs provider repair; replacing the GitHub token will not fix it. Do not disable certificate verification or send the token to support. |
A second pool reports PlanQuotaExceeded | Wait for the old pool to finish draining and deleting, then retry the new pool. The preview permits one pool per Project. |
| Service reports a GitHub repository verification refusal | Check that the repository is private and the fine-grained token targets that exact repository, has Administration: Read and write, has not expired, and has any required organization approval. Save a corrected token in the same ManagedSecret; the controller retries. |
Service reports that github_token contains whitespace or control characters | Replace the value with a file containing only the token, without a trailing newline or spaces. Save it to the same ManagedSecret; the pool retries. |
| Repository was made public | The controller pauses the pool until the repository is private and verification succeeds. After Ready returns, cancel and rerun jobs submitted while it was public; automatic replay of those queued jobs is not qualified. |
| Runner admission qualification is unavailable | Keep the pool and its workflow target. Contact Kube-DC support with the Project, pool name, and sanitized event. The provider must restore its verified topology and serving policies before new jobs can start; replacing the GitHub token will not fix this condition. |
| Job remains queued | Match runs-on to Connect, check Ready, available worker slots, and starting-worker events. GitHub owns the actual queue. |
| Service reports Capacity unavailable | A worker pod could not start. Check both project and organization CPU/memory quota against the pool's Settings reservation. Reduce worker size or ask Kube-DC support to review available capacity, then rerun the GitHub job. |
Service is Rejected with OrganizationQuotaExceeded | The Organization cannot reserve the active and replacement workers for this pool. Reduce worker size, free capacity held by another pool, or ask Kube-DC support to review quota; then retry creation. |
Service is Pending with NoQualifiedDataPlane and insufficient physical node headroom | No eligible node currently fits the worker, builder and listener footprint. More aggregate quota alone does not resolve this. Choose a smaller worker when creating a replacement pool, or ask Kube-DC support to review node capacity and idle reservations. Keep the OpenBao source until the old pool finishes normal deletion. |
| Buildx fails to start | Check project and organization quota, builder image pull, and temporary disk; compare the Settings reservation with the available limits. |
docker build or Compose fails | Use docker buildx build --push for a Dockerfile, or choose a different runtime for Compose/services. |
Service reports WorkerEvicted or WorkerExecutionFailed | Check project Events and the GitHub job. The workspace limit is 2 GiB and the shared process/thread limit is 4,096. If the interrupted job remains running at GitHub, cancel it there before retrying. The service reports the failure while ARC completes registration cleanup. |
Deletion reports DeregistrationBlocked | Keep the same ManagedSecret and save a valid token for the same repository with Administration: Read and write. Deletion retries automatically after the credential syncs. The service retains its cleanup credential until GitHub deregistration completes; do not remove finalizers or delete the source to force cleanup. Contact Kube-DC support if repository access cannot be restored. |
| Observation is stale or unavailable | Read the last-observation time and check Events. Displayed counts are the last known state. An idle historical graph can retain samples after a worker exits; no current sample does not mean zero usage. |
| Metrics show no current CPU | An idle pool has no worker pod to measure. Check the last observation and historical graph; a stale or unavailable message points to collection trouble. |
To delete a declarative pool, confirm the specific service, clear deletion protection, then delete the ManagedService:
kubectl annotate managedservice github-ci -n PROJECT_NAMESPACE \
services.kube-dc.com/confirm-delete=github-ci --overwrite
kubectl patch managedservice github-ci -n PROJECT_NAMESPACE --type=merge \
-p '{"spec":{"deletionProtection":false}}'
kubectl delete managedservice github-ci -n PROJECT_NAMESPACE
Deletion stops new workers and drains active work for up to 60 minutes before ARC cleanup. If GitHub is unavailable or the token is revoked, deletion stays pending and retains the cleanup credential. Restore GitHub connectivity or save a valid token in the same ManagedSecret, then wait for the service to disappear. The source ManagedSecret remains after successful deletion. Revoke the registration token in GitHub and delete the retained project secret when you no longer use it.