Quickstart

From an empty cluster to a recurring agent run in five minutes. The example below schedules a Claude Code agent that fixes failing PRs every six hours.

Prerequisites #

  • Kubernetes 1.28+ (kubectl context pointed at the target cluster)
  • Helm 3.16+
  • A Claude Code OAuth token in $CLAUDE_CODE_OAUTH_TOKEN
  • A GitHub PAT in $GH_TOKEN

Install with Helm #

bash
$ helm install thurkube oci://ghcr.io/thurbeen/charts/thurkube \
    --namespace thurkube-system --create-namespace

Verify the controller pod is ready and the CRDs are established:

bash
$ kubectl -n thurkube-system get pods
$ kubectl get crd -l app.kubernetes.io/managed-by=Helm \
    -o name | grep thurkube.thurbeen.eu

Create the Secret #

bash
$ kubectl create namespace agents
$ kubectl -n agents create secret generic claude-code-secrets \
    --from-literal=CLAUDE_CODE_OAUTH_TOKEN="$CLAUDE_CODE_OAUTH_TOKEN" \
    --from-literal=GH_TOKEN="$GH_TOKEN"

Define the building blocks #

Save the manifest below to agent.yaml . It declares one AgentRuntime (the container image and mount conventions), an AgentAuth (the env-var-to-Secret wiring), an AgentRole (allowed tools), a Repository , and a scheduled AgentJob that ties them together.

agent.yaml
apiVersion: thurkube.thurbeen.eu/v1alpha1
kind: AgentRuntime
metadata: { name: claude-code, namespace: agents }
spec:
  image: ghcr.io/thurbeen/claude-code-job:latest
  authEnvVar: CLAUDE_CODE_OAUTH_TOKEN
  configPath: /etc/claude-code-job
  persistPath: /var/lib/claude-code-job
---
apiVersion: thurkube.thurbeen.eu/v1alpha1
kind: AgentAuth
metadata: { name: claude-oauth, namespace: agents }
spec:
  secretRef: { name: claude-code-secrets, key: CLAUDE_CODE_OAUTH_TOKEN }
---
apiVersion: thurkube.thurbeen.eu/v1alpha1
kind: AgentRole
metadata: { name: default, namespace: agents }
spec:
  allowedTools: [Bash, Read, Edit, Write, Glob, Grep]
---
apiVersion: thurkube.thurbeen.eu/v1alpha1
kind: Repository
metadata: { name: thurkube, namespace: agents }
spec:
  owner: Thurbeen
  name:  thurkube
  tokenSecretRef: { name: claude-code-secrets, key: GH_TOKEN }
---
apiVersion: thurkube.thurbeen.eu/v1alpha1
kind: AgentJob
metadata: { name: pr-fixer, namespace: agents }
spec:
  schedule: "0 */6 * * *"
  timezone: Europe/Paris
  runtimeRef:     claude-code
  authRef:        claude-oauth
  roleRef:        default
  repositoryRefs: [thurkube]
  prompt: "Fix any failing PRs on this repository."
  persist: true

Apply & observe #

bash
$ kubectl apply -f agent.yaml
$ kubectl -n agents get aj -o wide
$ kubectl -n agents describe aj pr-fixer
$ kubectl -n agents get cronjob,job,configmap,sa,pvc \
    -l thurkube.thurbeen.eu/agentjob=pr-fixer

The Schedule , Suspended , Phase , and Last Run columns are declared as printer columns on the AgentJob CRD — no extra -o jsonpath required.

Cleanup #

Deleting the AgentJob triggers the controller's finalizer, which cascades to all owned children (CronJob, Job, ConfigMap, ServiceAccount, PVC).

bash
$ kubectl -n agents delete aj pr-fixer
$ helm uninstall thurkube -n thurkube-system