Tekton Pipelines — Architecture#
Architectural style#
Kubernetes Operator (Multi-Controller, Event-Driven, Level-Triggered)
Tekton is a Kubernetes operator built on the Knative reconciler framework. The core execution model is level-triggered reconciliation: controllers watch CRD objects (TaskRun, PipelineRun, ResolutionRequest) via informers, enqueue work items when resources change, and drive each resource toward its desired state in a ReconcileKind loop. There is no central dispatcher or message bus — state is stored entirely in Kubernetes API objects, making the system crash-safe and resumable.
The multi-controller design decomposes the system into single-responsibility controllers: the main controller binary hosts three reconcilers (TaskRun, PipelineRun, ResolutionRequest), a separate webhook binary handles admission, and the resolvers binary hosts five resolver controllers. All binaries share one Go module and vendor tree.
Component diagram (textual)#
┌──────────────────────────────────────────────────────────────────┐
│ cmd/controller │
│ ┌─────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ TaskRun │ │ PipelineRun │ │ Resolution │ │
│ │ Reconciler │ │ Reconciler │ │ Request │ │
│ │ pkg/reconciler/ │ │ pkg/reconciler/ │ │ Reconciler │ │
│ │ taskrun/ │ │ pipelinerun/ │ │ │ │
│ └────────┬─────────┘ └────────┬──────────┘ └───────────────┘ │
│ │ │ │
│ creates Pod creates TaskRun │
│ │ manages DAG │
└───────────┼─────────────────────┼──────────────────────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────────────────┐
│ Kubernetes Pod │ │ ResolutionRequest (CRD) │
│ ┌────────────┐ │ └──────────────┬───────────────┘
│ │ init: │ │ │
│ │ entrypoint │ │ cmd/resolvers ▼
│ │ (injected) │ │ ┌──────────────────────────────┐
│ ├────────────┤ │ │ Git / Hub / Bundle / Cluster │
│ │ step-0 │ │ │ HTTP Resolver Controllers │
│ │ (wrapped │ │ │ pkg/remoteresolution/resolver │
│ │ by entry │ │ └──────────────────────────────┘
│ │ point) │ │
│ ├────────────┤ │ cmd/webhook
│ │ step-1 │ │ ┌──────────────────────────────┐
│ │ ... │ │ │ Defaulting + Validation │
│ ├────────────┤ │ │ CRD Version Conversion │
│ │ sidecar: │ │ │ (v1alpha1/v1beta1/v1) │
│ │ nop / log │ │ └──────────────────────────────┘
│ │ results │ │
│ └────────────┘ │
└─────────────────┘Core components#
TaskRun Reconciler#
- Package:
pkg/reconciler/taskrun - Responsibility: Drives a
TaskRunobject from pending → running → complete. Creates Kubernetes Pods, monitors pod status, extracts step results, stops sidecars on completion, integrates SPIRE signing, emits CloudEvents and metrics. - Key types:
Reconcilerstruct (taskrun.go:80), implementstaskrunreconciler.Interface - Dependencies:
pkg/pod(Pod builder),pkg/remoteresolution(Task resolution),pkg/spire(signing),pkg/workspace,pkg/taskrunmetrics,pkg/trustedresources
PipelineRun Reconciler#
- Package:
pkg/reconciler/pipelinerun - Responsibility: Drives a
PipelineRunthrough DAG-scheduled task execution. Resolves the Pipeline definition, builds the DAG, identifies schedulable tasks, creates TaskRuns for ready tasks, aggregates results, propagates parameters and workspaces, handles cancellation and timeout. - Key types:
Reconcilerstruct (pipelinerun.go), usesdag.Graphfor scheduling - Dependencies:
pkg/reconciler/pipeline/dag,pkg/remoteresolution,pkg/substitution,pkg/workspace,pkg/pipelinerunmetrics
DAG Engine#
- Package:
pkg/reconciler/pipeline/dag - Responsibility: Builds a directed acyclic graph from pipeline task dependencies and
runAfterdeclarations.GetCandidateTasksreturns the set of tasks whose predecessors have all succeeded, driving scheduling. - Key types:
Graph,Node,Taskinterface - Dependencies: Pure logic, no Kubernetes dependencies
Pod Builder#
- Package:
pkg/pod - Responsibility: Translates a
TaskSpec+TaskRuninto a KubernetesPodspec. Injects theentrypointbinary as an init container, rewrites each step’s command to be wrapped by the entrypoint, mounts workspace volumes, applies credential init, configures resource requests (viapkg/internal/computeresources), creates results sidecars. - Key types:
Builderstruct withBuild(ctx, TaskRun, TaskSpec, ...Transformer) (*corev1.Pod, error)at pod.go:150 - Dependencies:
pkg/entrypoint,pkg/workspace,pkg/credentials,pkg/internal/computeresources
Resolution Framework#
- Package:
pkg/remoteresolution(current),pkg/resolution(legacy) - Responsibility: Decouples Task/Pipeline definition fetching from execution. Reconcilers submit a
ResolutionRequestCRD object; the resolution controller routes it to the appropriate resolver (git, hub, bundle, cluster, http). TheRequesterinterface abstracts this interaction. - Key types:
Requesterinterface (resource/request.go:45),Resolverinterface (framework/interface.go:29),CRDRequester(concrete implementation) - Dependencies:
ResolutionRequestCRD, Kubernetes API
Entrypoint Binary#
- Package:
pkg/entrypoint+cmd/entrypoint - Responsibility: Injected into every step container to enforce sequential execution within a Pod (normally containers run in parallel). Gates start by polling for a
post-filewritten by the previous step. Writes its own post-file on completion. Extracts step results and handles SPIRE result signing. - Key types:
Entrypointerstruct - Dependencies: File system (shared emptyDir volumes),
pkg/termination,pkg/spire
Admission Webhook#
- Package:
cmd/webhook+pkg/apis/pipeline/v1{,beta1,alpha1}(webhook logic) - Responsibility: Kubernetes admission webhook for defaulting (filling in defaults), validation (rejecting invalid objects), and CRD version conversion between v1alpha1, v1beta1, and v1. Built on Knative’s webhook infrastructure.
- Key types: All CRD types implement
knative.dev/pkg/webhookinterfaces (Defaultable,Validatable) - Dependencies:
knative.dev/pkg/webhook, all CRD type packages
Configuration Store#
- Package:
pkg/apis/config - Responsibility: Parses
config-defaultsandfeature-flagsConfigMaps into Go structs. Propagates live config viacontext.Contextusing aStorebacked by Knative’sconfigmap.Watcher. Reconcilers callconfig.FromContext(ctx)to read current config. - Key types:
Store,Defaults,FeatureFlags - Dependencies:
knative.dev/pkg/configmap
Data flow#
Typical PipelineRun execution trace:
1. User applies PipelineRun YAML → Kubernetes API server
2. PipelineRun Reconciler enqueued by informer event
3. ReconcileKind: fetch VerificationPolicies, build getPipelineFunc
4. Submit ResolutionRequest CRD for Pipeline definition (if remote)
→ Resolution controller routes to git/bundle/cluster resolver
→ Resolver fetches YAML, writes ResolvedResource to ResolutionRequest status
5. Reconciler reads resolved Pipeline; builds DAG (dag.Build)
6. dag.GetCandidateTasks → identifies tasks with no pending predecessors
7. For each schedulable task:
a. Submit ResolutionRequest for Task definition (if remote)
b. Apply parameter substitution (pkg/substitution)
c. Bind workspaces (pkg/workspace)
d. Create TaskRun object → Kubernetes API server
8. TaskRun Reconciler enqueued for each new TaskRun
9. ReconcileKind: resolve Task definition (via ResolutionRequest)
→ Verify trusted resource (pkg/trustedresources, Sigstore)
10. pod.Builder.Build(ctx, taskRun, taskSpec):
a. Rewrite step commands → entrypoint wrappers
b. Inject entrypoint init container (copies binary into Pod)
c. Mount workspace volumes
d. Add creds-init volume/container
e. Create results sidecar (sidecarlogresults)
→ Returns corev1.Pod spec
11. Create Pod → Kubernetes API server
12. Inside Pod:
a. entrypoint init container copies binary to /tekton/bin/entrypoint
b. step-0 container starts: entrypoint polls for "start file" (already present for first step)
c. step-0 executes user command, writes results to /tekton/results/
d. entrypoint writes post-file for step-0
e. step-1 entrypoint sees post-file → unblocked → runs
f. ... sequential execution enforced by file semaphores
13. TaskRun reconciler watches Pod status changes:
a. Reads termination messages → extract step results
b. SPIRE: sign results if configured
c. Updates TaskRun status with step statuses and results
14. PipelineRun reconciler sees TaskRun status change (via informer):
a. Updates DAG: marks task as done
b. Runs dag.GetCandidateTasks again → new schedulable tasks unlocked
c. Repeat from step 7 for newly schedulable tasks
15. When all tasks done: aggregate pipeline results, update PipelineRun status
16. Metrics recorded (Prometheus); CloudEvents emitted; OTel spans closedInitialization / Bootstrap#
cmd/controller/main.go uses Knative’s sharedmain.MainWithConfig:
- Flag parsing:
injection.ParseAndGetRESTConfigOrDie()— parses Kubernetes in-cluster config and custom flags (image refs, thread counts, namespace scope). - Context setup:
signals.NewContext()for graceful shutdown;filteredinformerfactory.WithSelectorsto scope informers by label. - Controller factories: Three factory functions are passed to
sharedmain:taskrun.NewController(opts, clock)pipelinerun.NewController(opts, clock)resolutionrequest.NewController(clock)
- Knative sharedmain: Initializes informer factories, sets up leader election (HA), starts the work queue, and calls each factory with
(ctx, configmap.Watcher). - Per-controller init:
- Extracts clients/informers from
ctx(Knative injection pattern) - Creates
config.Storeand callsWatchConfigs(cmw)to watch live ConfigMaps - Registers event handlers: informers enqueue items when resources change
- Returns
controller.Impl— the work queue and reconciler wrapper
- Extracts clients/informers from
Dependency injection pattern: Manual construction via Knative’s context-based injection. Clients and informers are registered into ctx before controllers are created; controllers extract them with typed Get(ctx) functions (e.g., kubeclient.Get(ctx), taskruninformer.Get(ctx)). No wire or dig — it is a form of service locator via context.Context.
Configuration#
- Mechanism: Kubernetes ConfigMaps watched live. Two main ConfigMaps:
config-defaults(default values for TaskRuns/PipelineRuns) andfeature-flags(experimental feature toggles). - Access pattern:
config.FromContext(ctx)inside reconcilers returns the currentConfigstruct. Theconfig.Store(Knativeconfigmap.Store) updates the config on every ConfigMap change without restart. - Binary configuration: Command-line flags for image references (
--entrypoint-image,--nop-image, etc.) and controller tuning (--threads-per-controller,--namespace). Environment variableTHREADS_PER_CONTROLLERalso accepted. - No Viper: Config is loaded via Knative configmap machinery, not Viper.
Key design decisions#
Knative reconciler framework as the foundation: By building on
knative.dev/pkg, Tekton inherits production-grade controller infrastructure (work queues, leader election, informer caching, configmap watching, metrics, structured logging). This was a deliberate early choice that accelerated development but creates a tight coupling to Knative’s opinionated patterns (context-based injection, configmap stores).File-based step sequencing within Pods: Kubernetes Pods run all containers concurrently. Tekton needs sequential steps. Rather than using Kubernetes Jobs (which create separate Pods per step, incurring scheduling overhead), the entrypoint binary enforces ordering within a single Pod using shared
emptyDirvolumes as semaphores. Each step waits for the previous step’s post-file before executing. This is architecturally unusual but avoids O(N) pod scheduling for N-step tasks.CRD-based resolution as an async protocol: Remote resource resolution is modeled as a CRD (
ResolutionRequest) rather than an in-process fetch. A reconciler creates the CRD, the resolution controller picks it up and writes the result back into the CRD status, and the original reconciler reads it on the next reconcile cycle. This asynchronous, level-triggered approach makes resolution crash-safe and observable, at the cost of extra API server round-trips.DAG-first pipeline scheduling: PipelineRun execution is driven purely by the DAG — tasks run as soon as their predecessors complete, maximizing parallelism. The reconciler re-evaluates the DAG on every TaskRun status change. There is no central scheduler or queue: the Kubernetes informer triggers the reconciler, which queries the DAG for what’s schedulable. This aligns naturally with the level-triggered reconciliation model.
Supply chain security baked in (not bolted on): SPIRE/SPIFFE workload identity and Sigstore verification are first-class components, not plugins. The
pkg/spireandpkg/trustedresourcespackages are imported directly by the reconcilers. This means trusted resource verification and result signing are enforced in the hot path of every TaskRun reconcile cycle when enabled via feature flags — a strong security posture but one that increases reconciler complexity and adds external service dependencies (SPIRE agent, KMS).