Skip to main content

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_target and 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 sizePool reservationOrganization worker requestsOrganization worker limitsWorker Pod slots
    1 vCPU / 2 GiB3.2 vCPU / 6.25 GiB3 vCPU / 6 GiB6 vCPU / 8 GiB2
    500m CPU / 1 GiB2.2 vCPU / 4.25 GiB2 vCPU / 4 GiB5 vCPU / 6 GiB2

    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​

  1. Open your project, then select Managed services → New service → GitHub Actions runners.
  2. 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.
  3. 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.
  4. Review the configuration and create the service. When it reports Ready, open Connect and copy the exact workflow target.
  5. 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​

SymptomCheck
Service waits for a credentialOpen 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 errorsContact 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 PlanQuotaExceededWait 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 refusalCheck 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 charactersReplace 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 publicThe 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 unavailableKeep 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 queuedMatch runs-on to Connect, check Ready, available worker slots, and starting-worker events. GitHub owns the actual queue.
Service reports Capacity unavailableA 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 OrganizationQuotaExceededThe 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 headroomNo 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 startCheck project and organization quota, builder image pull, and temporary disk; compare the Settings reservation with the available limits.
docker build or Compose failsUse docker buildx build --push for a Dockerfile, or choose a different runtime for Compose/services.
Service reports WorkerEvicted or WorkerExecutionFailedCheck 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 DeregistrationBlockedKeep 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 unavailableRead 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 CPUAn 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.