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 #
$ 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:
$ 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 #
$ 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.
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 #
$ 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).
$ kubectl -n agents delete aj pr-fixer
$ helm uninstall thurkube -n thurkube-system