Back to Software Engineering & Delivery Mastery Series CI/CD Platform Deep Dive

CI/CD Platform Deep Dive: Tekton

May 14, 2026 Wasil Zafar 42 min read

Master Tekton — Kubernetes-native CI/CD with Tasks, Pipelines, Triggers, custom resources, supply chain security with Tekton Chains, and cloud-native build patterns for containerized workloads at scale.

Table of Contents

  1. Introduction
  2. Architecture
  3. Tasks
  4. Pipelines
  5. TaskRuns & PipelineRuns
  6. Triggers
  7. Workspaces
  8. Tekton Chains
  9. Custom Tasks
  10. Platform Comparison
  11. Production Patterns
  12. Exercises

Introduction

Tekton is a powerful, flexible, Kubernetes-native framework for creating CI/CD pipelines. Originally developed by Google as part of the Knative project, Tekton was donated to the Continuous Delivery Foundation (CDF) in 2019 and has since become the standard for cloud-native CI/CD in Kubernetes-centric organizations.

Unlike platform-hosted CI/CD services (GitHub Actions, CircleCI, GitLab CI), Tekton runs entirely within your Kubernetes cluster. Pipelines are defined as Kubernetes Custom Resources — they're scheduled, executed, and monitored just like any other Kubernetes workload. This means your CI/CD inherits Kubernetes' scalability, resource management, and security model.

Key Insight: Tekton's fundamental design choice is to be a framework, not a product. It provides primitive building blocks (Tasks, Pipelines, Triggers) that platform teams assemble into custom CI/CD platforms. Companies like Red Hat (OpenShift Pipelines), IBM, and Google (Cloud Build) build their products on Tekton.

Why Kubernetes-Native Matters

Running CI/CD as Kubernetes workloads provides several advantages: pipelines scale with your cluster (no separate CI infrastructure), pipeline pods inherit cluster RBAC and network policies, resources are managed via standard Kubernetes tooling (kubectl, Helm, GitOps), and pipeline definitions follow Kubernetes API conventions (declarative YAML, status conditions, controller patterns).

The tradeoff is complexity — Tekton requires a Kubernetes cluster and Kubernetes expertise. For teams already running Kubernetes workloads, Tekton adds no new infrastructure. For teams without Kubernetes, the learning curve is steeper than hosted alternatives.

Architecture

Tekton's architecture consists of several installable components, each extending Kubernetes with new Custom Resource Definitions (CRDs):

Tekton Component Architecture
flowchart TD
    subgraph Core["Tekton Pipelines (Core)"]
        TASK[Task CRD]
        PIPE[Pipeline CRD]
        TR[TaskRun CRD]
        PR[PipelineRun CRD]
    end
    
    subgraph Triggers["Tekton Triggers"]
        EL[EventListener]
        TT[TriggerTemplate]
        TB[TriggerBinding]
        INT[Interceptor]
    end
    
    subgraph Ecosystem["Ecosystem"]
        CHAINS[Tekton Chains]
        DASH[Tekton Dashboard]
        CLI[tkn CLI]
        HUB[Tekton Hub]
    end
    
    WEBHOOK[Git Webhook] --> EL
    EL --> INT --> TB --> TT
    TT -->|Creates| PR
    PR -->|References| PIPE
    PIPE -->|Contains| TASK
    PR -->|Creates| TR
    
    CHAINS -->|Signs| TR
    DASH -->|Visualizes| PR
    HUB -->|Provides| TASK
    
    style Core fill:#132440,color:#fff
    style Triggers fill:#3B9797,color:#fff
    style Ecosystem fill:#16476A,color:#fff
                            
  • Tekton Pipelines — Core component. Installs Task, Pipeline, TaskRun, PipelineRun CRDs and the controller that executes them.
  • Tekton Triggers — Event-driven automation. EventListeners receive webhooks and create PipelineRuns based on TriggerTemplates.
  • Tekton Chains — Supply chain security. Automatically signs TaskRun/PipelineRun results and generates SLSA provenance attestations.
  • Tekton Dashboard — Web UI for visualizing and managing pipelines, runs, and resources.
  • tkn CLI — Command-line tool for interacting with Tekton resources (start runs, view logs, list tasks).
  • Tekton Hub — Catalog of reusable Tasks and Pipelines contributed by the community.
# Install Tekton Pipelines
kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml

# Install Tekton Triggers
kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml

# Install Tekton Chains
kubectl apply --filename https://storage.googleapis.com/tekton-releases/chains/latest/release.yaml

# Install Tekton Dashboard
kubectl apply --filename https://storage.googleapis.com/tekton-releases/dashboard/latest/release.yaml

# Install tkn CLI (Linux)
curl -LO https://github.com/tektoncd/cli/releases/latest/download/tkn_Linux_x86_64.tar.gz
tar xzf tkn_Linux_x86_64.tar.gz -C /usr/local/bin tkn

Tasks

A Task is the fundamental building block — a sequence of steps that run in a single pod. Each step runs as a container. Tasks are reusable, parameterized, and can produce results consumed by other tasks.

# Task: Build and push a container image
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: build-push-image
spec:
  params:
    - name: image
      type: string
      description: Full image name including registry
    - name: dockerfile
      type: string
      default: ./Dockerfile
    - name: context
      type: string
      default: .
  workspaces:
    - name: source
      description: Source code workspace
    - name: docker-credentials
      description: Docker registry credentials
  results:
    - name: IMAGE_DIGEST
      description: Digest of the built image
    - name: IMAGE_URL
      description: Full URL of the pushed image
  steps:
    - name: build-and-push
      image: gcr.io/kaniko-project/executor:latest
      env:
        - name: DOCKER_CONFIG
          value: $(workspaces.docker-credentials.path)
      args:
        - --dockerfile=$(params.dockerfile)
        - --context=$(workspaces.source.path)/$(params.context)
        - --destination=$(params.image)
        - --digest-file=$(results.IMAGE_DIGEST.path)
    - name: write-url
      image: alpine:3.19
      script: |
        #!/bin/sh
        echo -n "$(params.image)" > $(results.IMAGE_URL.path)
# Task: Run unit tests
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: run-tests
spec:
  params:
    - name: test-command
      type: string
      default: "npm test"
    - name: coverage-threshold
      type: string
      default: "80"
  workspaces:
    - name: source
  results:
    - name: COVERAGE
      description: Code coverage percentage
  steps:
    - name: install-deps
      image: node:20-alpine
      workingDir: $(workspaces.source.path)
      script: |
        #!/bin/sh
        npm ci --prefer-offline
    - name: run-tests
      image: node:20-alpine
      workingDir: $(workspaces.source.path)
      script: |
        #!/bin/sh
        set -e
        $(params.test-command) -- --coverage --coverageReporters=text
        
        # Extract coverage percentage
        COVERAGE=$(cat coverage/coverage-summary.json | \
          grep -o '"pct":[0-9.]*' | head -1 | cut -d: -f2)
        echo -n "$COVERAGE" > $(results.COVERAGE.path)
        
        # Fail if below threshold
        if [ "$(echo "$COVERAGE < $(params.coverage-threshold)" | bc)" -eq 1 ]; then
          echo "Coverage $COVERAGE% is below threshold $(params.coverage-threshold)%"
          exit 1
        fi

Pipelines

A Pipeline connects multiple Tasks in a directed acyclic graph (DAG). Tasks within a pipeline can run sequentially (using runAfter) or in parallel (default). Pipelines pass data between tasks via results and workspaces.

Tekton Pipeline Execution Flow
flowchart LR
    CLONE[git-clone] --> LINT[lint]
    CLONE --> TEST[run-tests]
    CLONE --> SCAN[security-scan]
    
    LINT --> BUILD[build-push-image]
    TEST --> BUILD
    SCAN --> BUILD
    
    BUILD --> DEPLOY[deploy-to-k8s]
    
    style CLONE fill:#3B9797,color:#fff
    style BUILD fill:#132440,color:#fff
    style DEPLOY fill:#BF092F,color:#fff
                            
# Complete CI/CD Pipeline
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: ci-cd-pipeline
spec:
  params:
    - name: repo-url
      type: string
    - name: revision
      type: string
      default: main
    - name: image
      type: string
    - name: namespace
      type: string
      default: production

  workspaces:
    - name: shared-workspace
    - name: docker-credentials
    - name: kube-config

  tasks:
    - name: clone
      taskRef:
        name: git-clone
      params:
        - name: url
          value: $(params.repo-url)
        - name: revision
          value: $(params.revision)
      workspaces:
        - name: output
          workspace: shared-workspace

    - name: lint
      taskRef:
        name: eslint
      runAfter: [clone]
      workspaces:
        - name: source
          workspace: shared-workspace

    - name: test
      taskRef:
        name: run-tests
      runAfter: [clone]
      params:
        - name: coverage-threshold
          value: "80"
      workspaces:
        - name: source
          workspace: shared-workspace

    - name: security-scan
      taskRef:
        name: trivy-scanner
      runAfter: [clone]
      workspaces:
        - name: source
          workspace: shared-workspace

    - name: build
      taskRef:
        name: build-push-image
      runAfter: [lint, test, security-scan]
      params:
        - name: image
          value: $(params.image)
      workspaces:
        - name: source
          workspace: shared-workspace
        - name: docker-credentials
          workspace: docker-credentials

    - name: deploy
      taskRef:
        name: kubectl-deploy
      runAfter: [build]
      params:
        - name: image
          value: $(params.image)@$(tasks.build.results.IMAGE_DIGEST)
        - name: namespace
          value: $(params.namespace)
      workspaces:
        - name: kube-config
          workspace: kube-config

  finally:
    - name: notify
      taskRef:
        name: slack-notification
      params:
        - name: status
          value: $(tasks.status)
        - name: pipeline-name
          value: $(context.pipelineRun.name)

TaskRuns & PipelineRuns

TaskRun and PipelineRun are the execution instances — they represent a single invocation of a Task or Pipeline with specific parameter values and workspace bindings. They're the Kubernetes equivalent of "running a job."

# PipelineRun — execute the CI/CD pipeline
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  generateName: ci-cd-run-
spec:
  pipelineRef:
    name: ci-cd-pipeline
  params:
    - name: repo-url
      value: https://github.com/myorg/myapp.git
    - name: revision
      value: abc123def
    - name: image
      value: registry.example.com/myapp:v1.2.3
    - name: namespace
      value: production
  workspaces:
    - name: shared-workspace
      volumeClaimTemplate:
        spec:
          accessModes: [ReadWriteOnce]
          resources:
            requests:
              storage: 1Gi
    - name: docker-credentials
      secret:
        secretName: docker-registry-creds
    - name: kube-config
      secret:
        secretName: kubeconfig-production
  taskRunTemplate:
    serviceAccountName: pipeline-runner
  timeouts:
    pipeline: 30m
    tasks: 20m
# Using tkn CLI to start and monitor runs
# Start a pipeline run
tkn pipeline start ci-cd-pipeline \
  --param repo-url=https://github.com/myorg/myapp.git \
  --param revision=main \
  --param image=registry.example.com/myapp:latest \
  --workspace name=shared-workspace,claimName=pipeline-pvc \
  --workspace name=docker-credentials,secret=docker-creds \
  --showlog

# List recent pipeline runs
tkn pipelinerun list

# Get logs from a specific run
tkn pipelinerun logs ci-cd-run-abc123 -f

# Describe run status
tkn pipelinerun describe ci-cd-run-abc123

Triggers

Tekton Triggers enable event-driven pipeline execution. When a Git webhook fires, the EventListener receives it, an Interceptor validates and filters the event, a TriggerBinding extracts parameters, and a TriggerTemplate creates the PipelineRun.

# EventListener — receives webhooks
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: github-listener
spec:
  serviceAccountName: tekton-triggers-sa
  triggers:
    - name: github-push
      interceptors:
        - ref:
            name: github
          params:
            - name: secretRef
              value:
                secretName: github-webhook-secret
                secretKey: token
            - name: eventTypes
              value: [push]
        - ref:
            name: cel
          params:
            - name: filter
              value: "body.ref == 'refs/heads/main'"
      bindings:
        - ref: github-push-binding
      template:
        ref: ci-cd-template

---
# TriggerBinding — extract data from webhook payload
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
  name: github-push-binding
spec:
  params:
    - name: repo-url
      value: $(body.repository.clone_url)
    - name: revision
      value: $(body.after)
    - name: repo-name
      value: $(body.repository.name)

---
# TriggerTemplate — creates PipelineRun from extracted data
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
  name: ci-cd-template
spec:
  params:
    - name: repo-url
    - name: revision
    - name: repo-name
  resourcetemplates:
    - apiVersion: tekton.dev/v1
      kind: PipelineRun
      metadata:
        generateName: $(tt.params.repo-name)-run-
      spec:
        pipelineRef:
          name: ci-cd-pipeline
        params:
          - name: repo-url
            value: $(tt.params.repo-url)
          - name: revision
            value: $(tt.params.revision)
          - name: image
            value: registry.example.com/$(tt.params.repo-name):$(tt.params.revision)
        workspaces:
          - name: shared-workspace
            volumeClaimTemplate:
              spec:
                accessModes: [ReadWriteOnce]
                resources:
                  requests:
                    storage: 1Gi

Workspaces

Workspaces provide shared storage for steps within a Task and between Tasks in a Pipeline. They abstract the storage mechanism — the same Pipeline can use PVCs in production and emptyDirs in testing.

Workspace Storage Options

PersistentVolumeClaim: Durable storage that persists across step/pod restarts. Required when Tasks in a Pipeline need to share large artifacts (source code, build outputs). Use volumeClaimTemplate for per-run PVCs or reference existing claims.

emptyDir: Ephemeral, pod-local storage. Fast but lost when the TaskRun pod terminates. Good for scratch space within a single Task. Can be memory-backed for extremely fast I/O.

ConfigMap: Read-only configuration data. Useful for providing config files (nginx.conf, settings) to pipeline steps.

Secret: Read-only sensitive data. Required for credentials (Docker registry auth, kubeconfig, API keys).

# Workspace binding examples in a PipelineRun
spec:
  workspaces:
    # PVC template (created per run, auto-cleaned)
    - name: shared-workspace
      volumeClaimTemplate:
        spec:
          accessModes: [ReadWriteOnce]
          storageClassName: fast-ssd
          resources:
            requests:
              storage: 5Gi

    # Existing PVC (shared across runs — use with caution)
    - name: maven-cache
      persistentVolumeClaim:
        claimName: maven-repo-cache

    # emptyDir (fast, ephemeral)
    - name: temp-workspace
      emptyDir: {}

    # Memory-backed emptyDir (RAM disk)
    - name: fast-scratch
      emptyDir:
        medium: Memory
        sizeLimit: 256Mi

    # Secret for credentials
    - name: docker-credentials
      secret:
        secretName: registry-auth

    # ConfigMap for configuration
    - name: app-config
      configMap:
        name: pipeline-config

Tekton Chains

Tekton Chains provides supply chain security by automatically signing TaskRun and PipelineRun results and generating SLSA provenance attestations. It proves that artifacts were produced by specific pipelines from specific source commits — a critical requirement for software supply chain compliance.

# Configure Tekton Chains for cosign signing
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{
  "artifacts.taskrun.format": "in-toto",
  "artifacts.taskrun.storage": "oci",
  "artifacts.oci.storage": "oci",
  "transparency.enabled": "true",
  "signers.x509.fulcio.enabled": "true"
}}'

# Verify a signed image with cosign
cosign verify \
  --certificate-identity="https://tekton.dev/chains/*" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  registry.example.com/myapp:v1.2.3

# View SLSA provenance
cosign verify-attestation \
  --type slsaprovenance \
  --certificate-identity-regexp=".*" \
  --certificate-oidc-issuer-regexp=".*" \
  registry.example.com/myapp:v1.2.3 | jq '.payload' | base64 -d | jq .
SLSA Compliance: Tekton Chains can achieve SLSA Level 3 provenance — meaning the build platform is hardened, provenance is non-falsifiable, and the build process is isolated. This is increasingly required by enterprise customers and government agencies (per NIST SSDF and Executive Order 14028).

Custom Tasks

Tekton's extensibility allows creating custom Task types via Kubernetes controllers. Custom Tasks handle operations that don't fit the standard step-based model — approval gates, external service calls, or specialized execution environments.

# Custom Task: Approval Gate
apiVersion: custom.tekton.dev/v1alpha1
kind: ApprovalTask
metadata:
  name: production-approval
spec:
  approvers:
    - team: platform-engineering
    - team: security
  minimumApprovals: 2
  timeout: 24h
  message: |
    Deployment to production requires approval.
    Pipeline: $(context.pipelineRun.name)
    Image: $(params.image)

---
# Using custom task in a Pipeline
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: deploy-with-approval
spec:
  tasks:
    - name: build
      taskRef:
        name: build-push-image
    - name: approve
      taskRef:
        apiVersion: custom.tekton.dev/v1alpha1
        kind: ApprovalTask
      runAfter: [build]
    - name: deploy
      taskRef:
        name: kubectl-deploy
      runAfter: [approve]

Platform Comparison

Understanding where Tekton fits relative to other CI/CD platforms helps inform adoption decisions:

DimensionTektonGitHub ActionsJenkinsArgo Workflows
RuntimeKubernetes podsHosted VMsAgent JVMKubernetes pods
DefinitionKubernetes CRDsYAML in .github/JenkinsfileKubernetes CRDs
ScalingKubernetes nativeGitHub-managedManual/pluginsKubernetes native
Supply ChainChains (SLSA L3)Attestations (SLSA L3)PluginsLimited
Best ForPlatform teams, complianceGitHub-native projectsLegacy enterpriseComplex DAGs, ML
Setup EffortHigh (K8s required)ZeroMediumHigh (K8s required)
EcosystemTekton HubMarketplace (largest)Plugins (extensive)Limited
Migration from Jenkins: Tekton is often the natural migration target for Jenkins shops moving to Kubernetes. The mental model maps well: Jenkins Pipeline stages → Tekton Pipeline tasks, Jenkins shared libraries → Tekton Hub tasks, Jenkins agents → Kubernetes pod execution.

Production Patterns

Running Tekton in production requires attention to multi-tenancy, resource management, and operational patterns:

# Multi-tenant pipeline with resource quotas
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-pipeline-quota
  namespace: team-a-pipelines
spec:
  hard:
    requests.cpu: "8"
    requests.memory: 16Gi
    limits.cpu: "16"
    limits.memory: 32Gi
    persistentvolumeclaims: "10"
    pods: "20"

---
# LimitRange for pipeline pods
apiVersion: v1
kind: LimitRange
metadata:
  name: pipeline-limits
  namespace: team-a-pipelines
spec:
  limits:
    - type: Container
      default:
        cpu: 500m
        memory: 512Mi
      defaultRequest:
        cpu: 100m
        memory: 128Mi
      max:
        cpu: "4"
        memory: 8Gi
# Pipeline-as-Code with Tekton (using Pipelines-as-Code controller)
# .tekton/pull-request.yaml — lives in the application repo
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: pull-request
  annotations:
    pipelinesascode.tekton.dev/on-event: "[pull_request]"
    pipelinesascode.tekton.dev/on-target-branch: "[main]"
    pipelinesascode.tekton.dev/max-keep-runs: "5"
spec:
  pipelineSpec:
    tasks:
      - name: clone
        taskRef:
          name: git-clone
          kind: ClusterTask
        params:
          - name: url
            value: "{{ repo_url }}"
          - name: revision
            value: "{{ revision }}"
        workspaces:
          - name: output
            workspace: source
      - name: test
        runAfter: [clone]
        taskSpec:
          steps:
            - image: node:20
              script: |
                cd $(workspaces.source.path)
                npm ci
                npm test
          workspaces:
            - name: source
        workspaces:
          - name: source
            workspace: source
  workspaces:
    - name: source
      volumeClaimTemplate:
        spec:
          accessModes: [ReadWriteOnce]
          resources:
            requests:
              storage: 1Gi
Tekton Multi-Tenant Architecture
flowchart TD
    subgraph Cluster["Kubernetes Cluster"]
        subgraph Control["Control Plane"]
            TC[Tekton Controller]
            TT[Tekton Triggers]
            TD[Tekton Dashboard]
        end
        
        subgraph TeamA["Namespace: team-a-pipelines"]
            PA[PipelineRun A]
            QA[ResourceQuota]
        end
        
        subgraph TeamB["Namespace: team-b-pipelines"]
            PB[PipelineRun B]
            QB[ResourceQuota]
        end
        
        subgraph Shared["Namespace: tekton-shared"]
            CT[ClusterTasks]
            SEC[Shared Secrets]
        end
    end
    
    TC --> PA
    TC --> PB
    CT -.-> PA
    CT -.-> PB
    
    style Control fill:#132440,color:#fff
    style TeamA fill:#3B9797,color:#fff
    style TeamB fill:#16476A,color:#fff
    style Shared fill:#BF092F,color:#fff
                            
  • Namespace isolation — Each team gets their own namespace with ResourceQuotas and LimitRanges. ClusterTasks provide shared reusable tasks.
  • PriorityClasses — Assign PriorityClasses to pipeline pods so production deploys preempt dev builds during resource pressure.
  • Cleanup policies — Use Tekton's built-in pruner or CronJobs to delete completed PipelineRuns after retention period.
  • Affinity/tolerations — Route pipeline workloads to dedicated node pools to avoid interfering with application pods.

Exercises

Exercise 1: Complete Tekton CI Pipeline

Install Tekton Pipelines in a local Kind/k3d cluster. Create Tasks for: git-clone (from Tekton Hub), running Go tests, building a container image with Kaniko, and deploying to the same cluster. Wire them into a Pipeline with proper workspace sharing. Create a PipelineRun and verify the application deploys successfully.

Exercise 2: Webhook-Driven Automation

Set up Tekton Triggers with a GitHub EventListener. Configure an Interceptor to validate webhook signatures and filter for push events on the main branch. Create TriggerBinding and TriggerTemplate that extract the commit SHA and repository URL, then automatically create PipelineRuns. Test by pushing a commit and verifying the pipeline starts.

Exercise 3: Supply Chain Security with Chains

Install Tekton Chains alongside your pipeline. Configure it to sign TaskRun results using cosign (keyless with Fulcio). Build and push a container image, then verify the signature and SLSA provenance attestation using cosign verify and cosign verify-attestation. Document what SLSA level your setup achieves.

Exercise 4: Multi-Tenant Pipeline Platform

Design a multi-tenant Tekton platform for 3 teams. Create separate namespaces with ResourceQuotas (CPU: 4 cores, Memory: 8GB per team). Implement ClusterTasks for common operations (git-clone, docker-build, kubectl-deploy). Set up RBAC so each team can only create PipelineRuns in their namespace but can reference shared ClusterTasks. Add PriorityClasses so production deploys take precedence over CI builds.