TaskRuns & Pods
The lifecycle is one condition
taskruntaskrun-succeeded-conditiontaskrun-reasontaskrun-start-completioncondition Succeeded: the TaskRun state machine
t = 0.0sstatus:conditions:- type: Succeededstatus: "Unknown"reason: StartedstartTime: "2026-10-07T10:00:00Z"
Try: play the happy path, then pick a scenario or press “make build exit 1” mid-run. Every outcome is the same condition (type Succeeded) with a different status and reason: Unknown while it runs, then True or False. Note ExceededNodeResources keeps status Unknown: the TaskRun waits for capacity instead of failing.
Anatomy of a TaskRun Pod
tekton-init-containerstaskrun-podnametaskrun-step-statestaskrun-termination-messagePod build-run-pod: one container per step
pod phase: —kind: Podmetadata:name: build-run-podlabels: {tekton.dev/taskRun: build-run}ownerReferences: [{kind: TaskRun, name: build-run}]spec:initContainers:- name: prepare # entrypoint → /tekton/bin- name: place-scripts # script: → /tekton/scriptscontainers:- name: step-clonecommand: [/tekton/bin/entrypoint]args: [-wait_file, /tekton/downward/ready,-post_file, /tekton/run/0/out, ...]- name: step-buildcommand: [/tekton/bin/entrypoint]args: [-wait_file, /tekton/run/0/out,-post_file, /tekton/run/1/out, ...]- name: step-testcommand: [/tekton/bin/entrypoint]args: [-wait_file, /tekton/run/1/out,-post_file, /tekton/run/2/out, ...]- name: step-pushcommand: [/tekton/bin/entrypoint]args: [-wait_file, /tekton/run/2/out,-post_file, /tekton/run/3/out, ...]- name: sidecar-registry # starts with steps
status:conditions:- type: Succeededstatus: "Unknown"reason: StartedstartTime: "2026-10-07T10:00:00Z"
Try: play it and watch each step's entrypoint block on the previous step's /tekton/run/N/out file. Then make step-test exit 1: it writes out.err and push is skipped without running its command. Add onError: continue: the failure is recorded (terminationReason Continued) and push runs.
TaskRun build-run against its deadline
apiVersion: tekton.dev/v1kind: TaskRunmetadata:name: build-runspec:taskRef: {name: build}timeout: "1h0m0s" # = the default---# ConfigMap config-defaults (tekton-pipelines ns)data:default-timeout-minutes: "60"
Try: with the defaults the work needs about 65 minutes and the 60-minute default kills it near the end of the tests. The controller deletes the Pod, so the running step never writes its out file. Raise the timeout, or set it to 0 to turn the deadline off.
100 PipelineRuns · each square is one attempt (one Pod)
top row = first attempt; red = failed attempt, green = passed. A column with no green is a failed PipelineRun.
P(TaskRun eventually succeeds) = 1 − pʳ⁺¹
# in the Pipelinetasks:- name: testretries: 2taskRef: {name: go-test}---# TaskRun ci-run-test after attempt 3status:conditions:- {type: Succeeded, status: "True", reason: Succeeded}podName: ci-run-test-pod-retry2retriesStatus:- conditions: [{status: "False", reason: Failed}]podName: ci-run-test-pod- conditions: [{status: "False", reason: Failed}]podName: ci-run-test-pod-retry1
Try: at 30% flakiness one retry turns 70% green into 91%, two into 97%. Now drag P to 100% (a real bug): retries only multiply the Pods you pay for. Each attempt keeps the same TaskRun; the earlier attempts move into status.retriesStatus.
build-run-pod
t = 0.0skind: TaskRunmetadata: {name: build-run}spec:taskRef: {name: build}debug:breakpoints:onFailure: "enabled"
$ tkn taskrun logs -f build-run
(nothing yet)Try: play to the failure with the breakpoint on. The TaskRun stays Running and the Pod stays alive, so you can exec in. Run debug-continue and push still runs. Turn the breakpoint off and the failure ends the run at once. Cancel mid-step and the Pod and its logs are deleted.
node-a
pool=generalnode-b
pool=generalnode-c
pool=ciPending Pods · 0
t = 0.0skind: TaskRunspec:taskRef: {name: build}podTemplate: {}---# in Task buildsteps:- name: buildcomputeResources:requests: {cpu: "1"}
Try: start TaskRuns, then raise the CPU request to 2.5: only one Pod fits per node and a queue forms. Turn on the nodeSelector alone: the only matching node is tainted, so nothing schedules until you add the toleration.
serviceAccountName, decides its credentials. That is covered in Auth & Pod config.apiVersion: tekton.dev/v1kind: TaskRunmetadata:generateName: build-run-spec:taskRef:name: build # looked up at startparams:- {name: package, value: ./cmd/server}
Task build in the cluster
kind: Taskmetadata:name: buildgeneration: 2spec:steps:- name: buildimage: golang:1.22
With taskRef, each new TaskRun resolves this object when it starts.
TaskRuns · status.taskSpec snapshot
t = 0s- Create a TaskRun.
Try: create a TaskRun, apply a new Task image while it runs, then create another one. The first keeps the spec it copied into status.taskSpec. Now delete the Task and create a third: CouldntGetTask. Switch to inline taskSpec and the Task object no longer matters.
- takeawayA TaskRun's state is one condition, Succeeded: Unknown → True/False, plus a reason. Timeouts, cancellations, missing Tasks and failing steps are all just different reasons for False.
- takeawayOne TaskRun attempt is one Pod. Init containers prepare the entrypoint, the entrypoint orders the steps through files in /tekton/run, and status.steps[] mirrors the container states.
- takeawayThe controller enforces the edges: it deletes the Pod on timeout or cancel, makes a new Pod per retry, and keeps a failed container alive when you ask for a breakpoint. The cluster's capacity decides how long a run waits before any of that.
Key concepts · 19
book chapter 3- TaskRun
A namespaced object (tekton.dev/v1) that asks for one execution of a Task with concrete params and workspaces.
Tasks are templates; a TaskRun is what actually runs. The TaskRun controller reconciles it into exactly one Pod (per attempt) and writes the result back into its status, so the TaskRun is also the record of what happened.
kind: TaskRuntkn task start buildkubectl create -f taskrun.yaml↑ see it in the visualization - The Succeeded condition
status.conditions[type=Succeeded], whose status is Unknown while the run is in progress and True or False once it has finished.
Every outcome (success, failure, timeout, cancellation) is the same condition with a different status and reason. Tools such as tkn, the Dashboard and the PipelineRun controller only have to read this one field.
status.conditions[0].statuskubectl wait --for=condition=Succeeded tr/x↑ see it in the visualization - reason and message
A machine-readable reason on the Succeeded condition (Started, Pending, Running, Succeeded, Failed, TaskRunTimeout, TaskRunCancelled, CouldntGetTask, ExceededNodeResources, ExceededResourceQuota…) plus a human message.
The status says whether the run is done. The reason says why: a timeout and a failing test are both False but need different fixes. Some reasons, like ExceededNodeResources, keep the status Unknown because the run is waiting rather than failed.
reason: TaskRunTimeoutreason: CouldntGetTaskreason: ExceededNodeResources↑ see it in the visualization - status.podName
The name of the Pod created for the current attempt, normally <taskrun>-pod (or -pod-retryN on retries).
It connects the Tekton view to the Kubernetes view: kubectl describe pod, events, node placement and container logs all hang off this name.
status.podNamekubectl describe pod build-run-pod↑ see it in the visualization - startTime and completionTime
Timestamps set when the controller first starts the TaskRun and when it reaches a final status.
The timeout is measured from startTime. Time spent waiting for a node or pulling images counts too, so a cluster that is short on capacity can time out runs that never got to execute.
status.startTimestatus.completionTime↑ see it in the visualization - Init containers (prepare, place-scripts, working-dir-initializer)
Containers Tekton adds before the steps: prepare copies the entrypoint binary into a shared volume, place-scripts writes step scripts to /tekton/scripts, and working-dir-initializer creates workingDirs.
They set up the shared /tekton volumes that let ordinary images behave as ordered steps. While they run, the Pod is Pending and the TaskRun reason is Pending.
prepareplace-scriptsworking-dir-initializer/tekton/bin/entrypoint↑ see it in the visualization - status.steps[]
One entry per step, copied from that container's state: waiting, running, or terminated with exitCode, plus Tekton's terminationReason.
terminationReason (Completed, Error, Continued, Skipped, TimeoutExceeded) shows how a step ended, which the raw exit code can't: a skipped step and a failed step both exit 1.
status.steps[].terminated.exitCodestatus.steps[].terminationReason↑ see it in the visualization - Termination message
A small file each step's entrypoint writes on exit (/tekton/termination) that Kubernetes copies into the container status.
This is how step timing and results get from inside the Pod to the controller without any extra network calls. It is also why results must stay small: the message is capped at about 4 KB per container.
terminationMessagePath: /tekton/terminationresults↑ see it in the visualization - spec.timeout
The maximum duration of the TaskRun, defaulting to 60 minutes (default-timeout-minutes in config-defaults). 0 disables it.
When it elapses the controller marks the run False/TaskRunTimeout and deletes the Pod, so nothing hangs forever. Builds that legitimately take longer need an explicit timeout.
spec.timeout: "1h30m"config-defaults: default-timeout-minutessteps[].timeout↑ see it in the visualization - retries
How many more times to try a failed run: set as retries on a Pipeline task, which the PipelineRun passes to the TaskRun it creates.
Each attempt is a new Pod. Retries hide flaky infrastructure or tests, but they make a real bug cost retries+1 runs before it is reported.
tasks[].retries: 2<taskrun>-pod-retry1↑ see it in the visualization - status.retriesStatus
A list holding the full status (conditions, podName, steps) of each earlier failed attempt.
The top-level status only describes the latest attempt. Without retriesStatus you'd lose the evidence of every failure that a later attempt papered over.
status.retriesStatus[].podName↑ see it in the visualization - Cancellation
Patching spec.status to TaskRunCancelled (what tkn taskrun cancel does) stops the run.
Cancellation is declarative like everything else: you change the spec and the controller acts. It marks the run False/TaskRunCancelled and deletes the Pod, taking its logs with it unless you store them elsewhere.
spec.status: "TaskRunCancelled"tkn taskrun cancel build-run↑ see it in the visualization - debug.breakpoints.onFailure
An alpha TaskRun option that keeps a failed step's container alive instead of ending the run.
You can kubectl exec into the exact environment that failed, then run debug-continue or debug-fail-continue from /tekton/debug/scripts to let the run go on. This beats guessing from logs.
debug.breakpoints.onFailure: "enabled"/tekton/debug/scripts/debug-continue↑ see it in the visualization - Step logs
Each step is a container, so its stdout/stderr are that container's logs. tkn taskrun logs -f follows them step by step.
Logs live with the Pod. Once the Pod is gone (after cancel, timeout or cluster cleanup) they are gone too, unless a log store such as Tekton Results keeps them.
tkn taskrun logs -f build-runkubectl logs build-run-pod -c step-test↑ see it in the visualization - Pod scheduling and ExceededNodeResources
The TaskRun Pod goes through the normal kube-scheduler. If no node has room for its resource requests, the run stays Unknown with reason ExceededNodeResources.
Tekton doesn't queue work itself; the cluster is the queue. Pending time counts against the timeout, so capacity problems can surface as timeouts.
reason: ExceededNodeResourcesreason: ExceededResourceQuotasteps[].computeResources↑ see it in the visualization - podTemplate
A TaskRun (or PipelineRun) field for Pod-level settings: nodeSelector, tolerations, affinity, securityContext, volumes and more.
It lets the same Task run on special nodes (ARM, GPUs, a dedicated CI pool) without changing the Task. A nodeSelector that only matches tainted nodes, without a toleration, leaves the Pod unschedulable.
spec.podTemplate.nodeSelectorspec.podTemplate.tolerationsconfig-defaults: default-pod-template↑ see it in the visualization - taskRef
A reference to a Task by name in the namespace, or through a resolver (git, bundles, hub, cluster).
It lets many runs share one reviewed definition. The spec is resolved when the run starts and copied into status.taskSpec, so editing the Task later doesn't change runs already in flight. A missing Task fails the run with CouldntGetTask.
spec.taskRef.namespec.taskRef.resolver: gitstatus.taskSpec↑ see it in the visualization - taskSpec (inline)
The whole Task definition embedded directly in the TaskRun.
Good for one-off or generated runs: nothing to install first and nothing to drift. The cost is no reuse, and nothing a reviewer can approve separately from the run.
spec.taskSpec.steps↑ see it in the visualization - serviceAccountName
The Kubernetes ServiceAccount the TaskRun Pod runs as. It decides which credentials and permissions the steps get.
It is the identity of the build. Its Secrets are what Tekton's credential initialization turns into git and docker config. The Auth chapter covers it in detail.
spec.serviceAccountName: ci-bot