Everything for Tekton in one place — pick a section below. 30 reviewed items across 2 content types.
Quick Answer
Tekton is a Kubernetes-native open-source framework for building CI/CD pipelines that run as custom resources directly on a Kubernetes cluster. Unlike Jenkins, which runs on a standalone server, or GitHub Actions, which is tied to GitHub's cloud infrastructure, Tekton defines pipelines as Kubernetes CRDs and executes each step in its own container pod.
Detailed Answer
Think of Tekton as the CI/CD engine that was born inside Kubernetes rather than bolted onto it. Jenkins grew up in the pre-container era, and GitHub Actions is a managed service locked inside GitHub. Tekton was designed from day one to speak Kubernetes natively, treating every pipeline step as a container running inside a pod, managed by the same scheduler and resource quotas that handle your production workloads.
Tekton is an open-source project originally developed at Google under the Knative project and later donated to the Continuous Delivery Foundation. It extends Kubernetes with a set of Custom Resource Definitions (CRDs) such as Task, TaskRun, Pipeline, PipelineRun, and Trigger. When you install Tekton on a cluster, the Tekton controller watches for these custom resources and orchestrates their execution. A Task defines a sequence of steps, and each step runs in its own container within a Kubernetes pod. A Pipeline chains multiple Tasks together, and a PipelineRun is the actual execution of that Pipeline. This is fundamentally different from Jenkins, where a Jenkinsfile describes stages that run on agents (either the controller node or separate worker nodes), or GitHub Actions, where workflow YAML files define jobs that execute on GitHub-hosted or self-hosted runners outside Kubernetes.
Under the hood, when you create a PipelineRun, the Tekton controller creates a TaskRun for each Task in the Pipeline. Each TaskRun becomes a Kubernetes pod, and each step within that Task becomes a container inside that pod. This means you get Kubernetes-native features for free: resource limits, node affinity, tolerations, service accounts, and RBAC all apply naturally. For example, when the payments-api team runs their build pipeline, Tekton schedules the build pod on a node with enough CPU and memory, using the same scheduling logic that places their production pods. Jenkins would need a separate plugin ecosystem to achieve even basic Kubernetes integration, and GitHub Actions requires self-hosted runners with custom configuration to run inside a cluster.
In production environments, Tekton shines when teams already run their applications on Kubernetes and want their CI/CD to live alongside their workloads. The order-processing-service team at a mid-size e-commerce company, for instance, can define their build, test, and deploy pipeline as Tekton resources stored in the same Git repository as their application code. Tekton Triggers can watch for GitHub webhooks and automatically create PipelineRuns, mimicking the event-driven model of GitHub Actions but running entirely within the cluster. The checkout-service team can reuse the same Task definitions for building container images, running integration tests, or deploying Helm charts across multiple pipelines.
One important distinction to keep in mind is the operational overhead trade-off. Jenkins and GitHub Actions abstract away the infrastructure: Jenkins gives you a web UI and plugin marketplace out of the box, and GitHub Actions requires zero infrastructure setup. Tekton requires you to manage the Tekton controller, maintain the cluster it runs on, handle storage for pipeline logs and artifacts, and build your own dashboard or use the optional Tekton Dashboard. This makes Tekton the stronger choice for Kubernetes-centric organizations that want full control but a heavier lift for small teams that just need a quick CI/CD solution without managing cluster infrastructure.
Code Example
# Install Tekton Pipelines on a Kubernetes cluster kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml # Verify Tekton controller pods are running kubectl get pods --namespace tekton-pipelines # Check Tekton CRDs are registered in the cluster kubectl get crds | grep tekton.dev # List available Tekton Tasks in the current namespace tkn task list # List available Tekton Pipelines in the current namespace tkn pipeline list # View the Tekton Pipelines controller logs for troubleshooting kubectl logs -n tekton-pipelines -l app=tekton-pipelines-controller # Compare: Jenkins runs as a standalone Java application # java -jar jenkins.war --httpPort=8080 # Compare: GitHub Actions uses YAML workflows in .github/workflows/ # No cluster installation needed for GitHub-hosted runners
Interview Tip
A junior engineer typically says 'Tekton is a CI/CD tool like Jenkins but for Kubernetes.' While directionally correct, interviewers want to hear about the Kubernetes-native architecture: Tasks and Pipelines are CRDs, steps run as containers inside pods, and everything is managed by the Kubernetes API server. Mention that Tekton uses the same scheduling, RBAC, and resource management as your production workloads. Contrast this with Jenkins running as a standalone Java process and GitHub Actions executing on external runners. If you can explain why a Kubernetes-centric team would choose Tekton over Jenkins or GitHub Actions, emphasizing the consistent tooling and no external dependencies, you demonstrate architectural awareness that goes beyond surface-level tool comparison.
◈ Architecture Diagram
┌──────────────────────────────────────────────┐ │ Kubernetes Cluster │ │ │ │ ┌───────────────────────────────────────┐ │ │ │ Tekton Controller │ │ │ │ (watches for CRDs, orchestrates) │ │ │ └───────────────┬───────────────────────┘ │ │ │ │ │ ┌─────────┴──────────┐ │ │ ↓ ↓ │ │ ┌──────────┐ ┌──────────┐ │ │ │ TaskRun │ │ TaskRun │ │ │ │ (Pod) │ │ (Pod) │ │ │ │ ┌──────┐ │ │ ┌──────┐ │ │ │ │ │Step 1│ │ │ │Step 1│ │ │ │ │ │Step 2│ │ │ │Step 2│ │ │ │ │ └──────┘ │ │ └──────┘ │ │ │ └──────────┘ └──────────┘ │ │ │ │ ● Tasks/Pipelines = Kubernetes CRDs │ │ ● Steps = Containers inside Pods │ │ ● Native RBAC, scheduling, resource limits │ └──────────────────────────────────────────────┘
💬 Comments
Quick Answer
A Task in Tekton is a reusable definition of a series of steps that execute sequentially inside a single Kubernetes pod. A TaskRun is the actual execution instance of a Task, similar to how a Kubernetes Deployment is a template and a Pod is a running instance.
Detailed Answer
Think of a Task as a recipe card in a kitchen and a TaskRun as actually cooking that recipe. The recipe card lists the ingredients (inputs), the steps to follow (build, test, push), and the final dish (outputs). You can cook the same recipe multiple times with different ingredients, and each cooking session is a separate TaskRun that runs independently.
A Tekton Task is a Kubernetes Custom Resource that defines a sequence of steps to perform a specific unit of work. Each Task YAML manifest specifies an apiVersion (tekton.dev/v1), a kind (Task), metadata including the name, and a spec containing the list of steps. Each step specifies a container image and a script or command to run. Steps execute sequentially within a single pod, sharing the same network namespace and, optionally, the same volumes. For example, the inventory-sync team might define a Task called build-and-test that has three steps: one to install dependencies using a Node.js image, one to run unit tests, and one to run linting. Since all three steps run in the same pod, they share the workspace filesystem, so the dependencies installed in step one are available in step two without any special configuration.
Under the hood, when you create a TaskRun, the Tekton controller translates the Task definition into a Kubernetes pod specification. Each step becomes an init container or a regular container within that pod, and Tekton uses an internal entrypoint binary to enforce sequential execution. The TaskRun resource tracks the status of each step, recording start times, completion times, exit codes, and logs. If any step fails (returns a non-zero exit code), subsequent steps are skipped and the TaskRun is marked as failed. You can view the status of a TaskRun using kubectl get taskrun or the tkn CLI tool. The payments-api team, for instance, can run tkn taskrun logs build-and-test-run-7xk2m to stream the logs from a specific TaskRun and debug a failing test step.
In production workflows, Tasks are designed to be reusable building blocks. A Task like build-docker-image can accept parameters for the image name, tag, and Dockerfile path, allowing the user-auth-service, checkout-service, and order-processing-service teams to all use the same Task with different parameter values. The Tekton Hub (hub.tekton.dev) provides a catalog of community-maintained Tasks for common operations like building with Kaniko, pushing to registries, deploying with kubectl, and running security scans. Teams install these Tasks into their cluster and reference them in Pipelines, avoiding the need to write everything from scratch.
A common beginner mistake is confusing Tasks with Pipelines. A Task is a single unit of work executed in one pod, while a Pipeline orchestrates multiple Tasks across multiple pods. Another gotcha is that steps within a Task always run sequentially and cannot be parallelized. If you need parallel execution, you must use separate Tasks within a Pipeline and configure them to run concurrently. Also, remember that a TaskRun is immutable once created: you cannot update a running TaskRun, you must cancel it and create a new one. This immutability is a deliberate design choice that ensures reproducibility and auditability of every pipeline execution.
Code Example
# Define a Task to build and test a Node.js application
apiVersion: tekton.dev/v1
# Kind specifies this is a Tekton Task resource
kind: Task
metadata:
# Name used to reference this Task in Pipelines and TaskRuns
name: build-and-test
spec:
# Parameters allow reuse with different values
params:
- name: app-name
type: string
description: Name of the application to build
# Steps execute sequentially in a single pod
steps:
- name: install-deps
# Container image for this step
image: node:18-alpine
# Script to run inside the container
script: |
cd $(workspaces.source.path)
npm ci
- name: run-tests
image: node:18-alpine
# Tests run after deps are installed in the same pod
script: |
cd $(workspaces.source.path)
npm test
- name: run-lint
image: node:18-alpine
# Linting is the final step before the Task completes
script: |
cd $(workspaces.source.path)
npm run lint
# Workspace provides shared filesystem for all steps
workspaces:
- name: source
description: The Git source code checkout
# Create a TaskRun to execute the Task
# kubectl apply -f taskrun.yaml
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
# Unique name for this execution instance
generateName: build-and-test-run-
spec:
# Reference the Task defined above
taskRef:
name: build-and-test
params:
- name: app-name
value: payments-api
workspaces:
- name: source
# PVC provides persistent storage for the workspace
persistentVolumeClaim:
claimName: payments-api-source-pvcInterview Tip
A junior engineer typically describes Tasks as 'a list of commands to run.' Interviewers expect you to explain the Kubernetes mapping: a Task becomes a pod, each step becomes a container, and steps share the pod's filesystem and network. Mention that TaskRuns are the execution instances that track status, exit codes, and logs. Highlight that Tasks are reusable with parameters, and reference the Tekton Hub as a source of community Tasks. If asked about failure handling, explain that a non-zero exit code in any step fails the entire TaskRun and skips remaining steps. Demonstrating you understand the Task-to-pod mapping and the immutability of TaskRuns shows genuine hands-on experience beyond reading documentation.
◈ Architecture Diagram
┌──────────────────────────────────────────┐ │ Task: build-and-test │ │ │ │ ┌────────────────────────────────────┐ │ │ │ TaskRun → Kubernetes Pod │ │ │ │ │ │ │ │ ┌────────────┐ │ │ │ │ │ Step 1 │ install-deps │ │ │ │ │ node:18 │──→ npm ci │ │ │ │ └─────┬──────┘ │ │ │ │ ↓ │ │ │ │ ┌────────────┐ │ │ │ │ │ Step 2 │ run-tests │ │ │ │ │ node:18 │──→ npm test │ │ │ │ └─────┬──────┘ │ │ │ │ ↓ │ │ │ │ ┌────────────┐ │ │ │ │ │ Step 3 │ run-lint │ │ │ │ │ node:18 │──→ npm run lint │ │ │ │ └────────────┘ │ │ │ │ │ │ │ │ ● All steps share filesystem │ │ │ │ ● Sequential execution only │ │ │ └────────────────────────────────────┘ │ └──────────────────────────────────────────┘
💬 Comments
Quick Answer
A Pipeline in Tekton orchestrates multiple Tasks into a directed acyclic graph (DAG), defining the order and dependencies between them. A PipelineRun is the execution instance of a Pipeline, creating individual TaskRuns for each Task and managing data flow between them through workspaces and results.
Detailed Answer
If a Task is a single workstation on an assembly line, a Pipeline is the entire factory floor layout that defines which workstations operate in which order, which ones can run in parallel, and how parts (data) move between stations. The PipelineRun is the actual production run where raw materials enter and a finished product comes out the other end.
A Tekton Pipeline is a Kubernetes Custom Resource that defines a collection of Tasks arranged in a specific execution order. Each Task within a Pipeline is referenced as a pipeline task (under the tasks array in the spec), and you can define dependencies between tasks using the runAfter field. Tasks without dependencies or runAfter constraints run in parallel by default. For example, the checkout-service team might define a Pipeline with four tasks: clone the source code, then run unit tests and security scanning in parallel (since neither depends on the other), and finally deploy to staging only after both tests and scanning succeed. This creates a DAG execution model where the Tekton controller determines the optimal execution order based on declared dependencies.
Internally, when you create a PipelineRun, the Tekton controller reads the Pipeline definition and creates a TaskRun for each pipeline task. Each TaskRun becomes its own Kubernetes pod, meaning tasks run in separate pods with separate filesystems and network namespaces. Data is shared between tasks through workspaces (backed by PersistentVolumeClaims) and results (small string values passed between tasks). The PipelineRun resource tracks the aggregate status: it is considered successful only if all TaskRuns succeed, and it fails if any required TaskRun fails. The user-auth-service team can inspect a PipelineRun to see which tasks completed, which are running, and which are pending, all through standard kubectl commands or the tkn CLI.
In production, Pipelines enable teams to model complex CI/CD workflows with clear separation of concerns. The order-processing-service team defines their deployment pipeline with stages for building the container image, running integration tests against a test database, scanning the image for vulnerabilities with Trivy, deploying to a staging environment, running smoke tests, and finally promoting to production with a manual approval gate. Each of these stages is a separate Task, and the Pipeline defines how they connect. Workspaces carry the source code and build artifacts through the pipeline, while results pass metadata like the built image digest from the build task to the deploy task.
A critical gotcha for beginners is understanding that tasks in a Pipeline run in separate pods, unlike steps within a Task that share a pod. This means you cannot simply write a file in one task and read it in the next unless they share a workspace backed by a PersistentVolumeClaim. Another common mistake is not setting runAfter correctly, which causes tasks to run in parallel when they should be sequential. Also, PipelineRuns support timeouts at both the pipeline level and individual task level, and beginners often forget to set these, leading to stuck pipelines that consume cluster resources indefinitely when a task hangs.
Code Example
# Define a Pipeline with sequential and parallel Tasks
apiVersion: tekton.dev/v1
# Kind specifies this is a Tekton Pipeline resource
kind: Pipeline
metadata:
# Name used to reference this Pipeline in PipelineRuns
name: checkout-service-pipeline
spec:
# Pipeline-level parameters passed down to Tasks
params:
- name: git-url
type: string
- name: image-name
type: string
# Workspaces shared across Tasks in the Pipeline
workspaces:
- name: shared-workspace
tasks:
# First task: clone the source code
- name: clone-source
taskRef:
name: git-clone
workspaces:
- name: output
workspace: shared-workspace
params:
- name: url
value: $(params.git-url)
# Second task: runs after clone, executes unit tests
- name: run-tests
taskRef:
name: build-and-test
runAfter:
- clone-source
workspaces:
- name: source
workspace: shared-workspace
# Third task: runs in parallel with tests (no runAfter dependency)
- name: security-scan
taskRef:
name: trivy-scan
runAfter:
- clone-source
workspaces:
- name: source
workspace: shared-workspace
# Fourth task: deploy only after tests and scan pass
- name: deploy-staging
taskRef:
name: kubectl-deploy
runAfter:
- run-tests
- security-scan
params:
- name: image
value: $(params.image-name)
# Create a PipelineRun to execute the Pipeline
# kubectl apply -f pipelinerun.yaml
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
# generateName creates unique names for each execution
generateName: checkout-service-run-
spec:
pipelineRef:
name: checkout-service-pipeline
params:
- name: git-url
value: https://github.com/acme/checkout-service.git
- name: image-name
value: registry.acme.io/checkout-service:latest
workspaces:
- name: shared-workspace
# PVC ensures data persists across Tasks in different pods
persistentVolumeClaim:
claimName: pipeline-workspace-pvcInterview Tip
A junior engineer typically says 'a Pipeline is a list of Tasks that run in order.' Interviewers want to hear about the DAG execution model: tasks without dependencies run in parallel, runAfter defines sequential constraints, and the controller optimizes execution order automatically. Emphasize that each task runs in a separate pod, so data sharing requires explicit workspaces backed by PersistentVolumeClaims. Mention results for passing small values like image digests between tasks. If asked about failure handling, explain that a PipelineRun fails when any required task fails, and you can configure finally tasks that run regardless of success or failure for cleanup operations. Demonstrating awareness of the parallel-by-default behavior and the workspace data-sharing requirement shows you have actually built and debugged real Tekton Pipelines.
◈ Architecture Diagram
┌──────────────────────────────────────────────┐ │ Pipeline: checkout-service-pipeline │ │ │ │ ┌──────────────┐ │ │ │ clone-source │ │ │ │ (git-clone) │ │ │ └──────┬───────┘ │ │ │ │ │ ┌────┴─────┐ │ │ ↓ ↓ │ │ ┌─────────┐ ┌──────────────┐ │ │ │run-tests│ │security-scan │ ← parallel │ │ │ │ │ (trivy) │ │ │ └────┬────┘ └──────┬───────┘ │ │ │ │ │ │ └──────┬───────┘ │ │ ↓ │ │ ┌────────────────┐ │ │ │ deploy-staging │ │ │ │ (kubectl-deploy)│ │ │ └────────────────┘ │ │ │ │ ● Each Task = separate Kubernetes Pod │ │ ● Shared workspace via PVC │ │ ● runAfter defines execution order │ └──────────────────────────────────────────────┘
💬 Comments
Quick Answer
Steps are the individual containers within a Tekton Task that execute sequentially inside a single Kubernetes pod. Steps share data through the pod's filesystem, primarily using Tekton Workspaces which mount shared volumes, and through Tekton Results which allow steps to write small string values that subsequent steps or tasks can reference.
Detailed Answer
Imagine steps as workers sitting at the same desk in an office. Each worker (step) does their job one after the other, and because they share the same desk (pod filesystem), the first worker can leave documents (files) for the next worker to pick up. The desk drawers (workspaces) are shared storage, and the sticky notes (results) are small messages passed along.
In Tekton, a step is the smallest unit of execution. Each step is defined inside a Task spec and specifies a container image, a script or command to run, and optionally resource limits, environment variables, and volume mounts. Steps within a Task always execute sequentially in the order they are defined. When the Tekton controller creates a pod for a TaskRun, each step becomes a container within that pod. Tekton uses an internal entrypoint mechanism to ensure that step two does not start until step one has completed successfully. For example, in the payments-api build task, the first step clones the repository, the second step compiles the code, and the third step runs tests. Each step uses a different container image suited to its purpose: a git image for cloning, a golang image for compiling, and a testing image for running tests.
The primary mechanism for sharing data between steps is the shared filesystem within the pod. By default, Tekton mounts an emptyDir volume at a well-known path, and workspaces provide additional shared mount points. When the inventory-sync team defines a workspace called source, all steps in the Task can read from and write to $(workspaces.source.path). The first step clones the Git repository into this path, and subsequent steps can access the source code, build artifacts, and test results written there. Because all steps run in the same pod, this filesystem sharing is fast and does not require any network transfer. Additionally, Tekton provides a special /tekton/results directory where steps can write small string values (up to 4096 bytes) that become TaskRun results, which can then be referenced by other tasks in a Pipeline.
In production scenarios, teams use steps to decompose complex operations into discrete, debuggable units. The user-auth-service team might have a security scanning task with four steps: download the vulnerability database, scan dependencies, scan the container image, and generate a compliance report. Each step uses a specialized container image, but they all share the same workspace where the scan results accumulate. If the dependency scan step fails, the team can inspect the logs for that specific step without wading through the output of the entire task. The step-level granularity also makes it easy to add retry logic or timeout settings to individual operations.
A common beginner pitfall is trying to share data between steps using environment variables set in one step and read in another. Environment variables are scoped to a single container and do not persist across steps. Instead, you must write values to files in the shared workspace or use Tekton results. Another gotcha is that each step runs as a separate container with its own filesystem root, so a binary installed in step one (say, in /usr/local/bin) is not available in step two if it uses a different container image. The shared workspace is the only reliable mechanism for passing data. Finally, steps cannot run in parallel within a Task. If you need concurrent execution, you must split the work into separate Tasks within a Pipeline.
Code Example
# Task demonstrating data sharing between steps
apiVersion: tekton.dev/v1
# This Task shows how steps communicate via shared workspace
kind: Task
metadata:
name: build-and-publish
spec:
# Workspace provides shared filesystem across all steps
workspaces:
- name: source
description: Shared directory for source code and artifacts
# Results allow passing small string values out of the Task
results:
- name: image-digest
description: The digest of the built container image
params:
- name: image-name
type: string
steps:
# Step 1: Clone repository into the shared workspace
- name: clone-repo
image: alpine/git:latest
script: |
git clone https://github.com/acme/payments-api.git \
$(workspaces.source.path)/repo
# Step 2: Build the application using files from Step 1
- name: build-app
image: golang:1.21
script: |
cd $(workspaces.source.path)/repo
go build -o $(workspaces.source.path)/app ./cmd/server
# Step 3: Build container image using binary from Step 2
- name: build-image
image: gcr.io/kaniko-project/executor:latest
script: |
/kaniko/executor \
--context=$(workspaces.source.path)/repo \
--destination=$(params.image-name) \
--digest-file=$(workspaces.source.path)/digest
# Step 4: Write the image digest to a Tekton Result
- name: export-digest
image: alpine:3.18
script: |
DIGEST=$(cat $(workspaces.source.path)/digest)
echo -n "$DIGEST" > $(results.image-digest.path)Interview Tip
A junior engineer typically says 'steps are commands that run in a task.' Interviewers want to hear the container model: each step is a separate container within the same pod, they share the pod network and workspace volumes, but each has its own filesystem root. Explain the two data-sharing mechanisms: workspaces for files and results for small string values. Mention that environment variables do not persist across steps because each step is a different container. If asked about debugging, explain that you can inspect logs per step using tkn taskrun logs with the step name, which gives fine-grained visibility. Showing awareness that binaries installed in one step's image are not available in another step's image demonstrates real troubleshooting experience that interviewers value.
◈ Architecture Diagram
┌──────────────────────────────────────────┐ │ Task: build-and-publish │ │ Runs as a single Pod │ │ │ │ ┌────────────────────────────────────┐ │ │ │ Shared Workspace Volume │ │ │ │ /workspace/source │ │ │ │ ┌──────┐ ┌─────┐ ┌────────┐ │ │ │ │ │ repo │ │ app │ │ digest │ │ │ │ │ └──────┘ └─────┘ └────────┘ │ │ │ └────────────────────────────────────┘ │ │ ↑ ↑ ↑ ↑ │ │ ┌────┴───┐ ┌───┴───┐ ┌───┴───┐ ┌───┴──┐ │ │ │Step 1 │→│Step 2 │→│Step 3 │→│Step 4│ │ │ │clone │ │build │ │image │ │digest│ │ │ │git │ │golang │ │kaniko │ │result│ │ │ └────────┘ └───────┘ └───────┘ └──────┘ │ │ │ │ ● Steps share workspace filesystem │ │ ● Results pass small values (< 4KB) │ │ ● Env vars do NOT persist across steps │ └──────────────────────────────────────────┘
💬 Comments
Quick Answer
Tekton is installed by applying its release YAML manifests to a Kubernetes cluster using kubectl apply. The core component is Tekton Pipelines, which can be extended with optional components like Tekton Triggers for event-driven execution, the Tekton Dashboard for a web UI, and the Tekton CLI (tkn) for command-line management.
Detailed Answer
Installing Tekton is like setting up a new department in an office building. The core Pipelines component is the department itself, Triggers is the receptionist who handles incoming requests, the Dashboard is the monitoring screen in the lobby, and the tkn CLI is the intercom system that lets you communicate with the department from anywhere in the building.
Tekton Pipelines is the foundational component and is installed by applying a single YAML manifest from the official Tekton release bucket on Google Cloud Storage. The manifest creates the tekton-pipelines namespace, deploys the Tekton controller and webhook pods, registers the Custom Resource Definitions (Tasks, TaskRuns, Pipelines, PipelineRuns, etc.), and sets up the necessary RBAC roles and service accounts. The installation requires a Kubernetes cluster running version 1.25 or later, and the cluster must have a default StorageClass configured if you plan to use PersistentVolumeClaim-backed workspaces. For a production cluster running the payments-api and order-processing-service workloads, the Tekton controller typically consumes modest resources (around 100m CPU and 256Mi memory) but should be monitored as the number of concurrent PipelineRuns increases.
Beyond the core Pipelines component, teams typically install additional Tekton components based on their needs. Tekton Triggers enables event-driven pipeline execution by listening for incoming webhooks (such as GitHub push events) and automatically creating PipelineRuns. It introduces additional CRDs like EventListener, TriggerBinding, and TriggerTemplate. The Tekton Dashboard provides a web-based UI for viewing and managing Tasks, Pipelines, and their runs, which is helpful for teams that prefer a graphical interface over kubectl commands. The Tekton CLI (tkn) is a separate binary installed on developer machines that provides convenient commands for listing, starting, and monitoring pipeline resources without writing kubectl commands.
In production environments, the checkout-service team at a typical organization would install Tekton Pipelines and Triggers on their shared development cluster. The EventListener is configured to receive webhooks from the Git repository, so every push to a feature branch automatically triggers the CI pipeline. The user-auth-service team uses the Tekton Dashboard to monitor pipeline executions and quickly identify which commit broke the build. Platform engineers install the tkn CLI on CI bastion hosts and developer laptops for quick troubleshooting and ad-hoc pipeline management.
A common gotcha during installation is forgetting to verify that the tekton-pipelines namespace pods are in a Running state before attempting to create Tasks or Pipelines. The webhook pod must be ready to validate incoming resources, and attempting to apply a Task before the webhook is ready results in validation errors. Another mistake is installing Tekton on a cluster without a default StorageClass, which causes PVC-backed workspaces to hang in a Pending state. Always run kubectl get storageclass to verify before installation. For air-gapped or restricted environments, you can download the release YAML and container images ahead of time and host them in an internal registry, modifying the manifest to reference internal image paths.
Code Example
# Install Tekton Pipelines (core component) kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml # Wait for Tekton Pipelines controller to be ready kubectl wait --for=condition=ready pod -l app=tekton-pipelines-controller -n tekton-pipelines --timeout=120s # Verify Tekton Pipelines pods are running kubectl get pods -n tekton-pipelines # Install Tekton Triggers for event-driven pipelines kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml # Install Tekton Interceptors used by Triggers kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml # Install Tekton Dashboard for web-based UI kubectl apply --filename https://storage.googleapis.com/tekton-releases/dashboard/latest/release.yaml # Access the Dashboard via port-forward kubectl port-forward -n tekton-pipelines svc/tekton-dashboard 9097:9097 # Install the Tekton CLI (tkn) on macOS brew install tektoncd-cli # Verify tkn CLI installation tkn version # Verify all Tekton CRDs are registered kubectl get crds | grep tekton.dev # Check cluster has a default StorageClass for workspaces kubectl get storageclass
Interview Tip
A junior engineer typically says 'you just kubectl apply the YAML.' While technically correct, interviewers want to hear about the full installation landscape: Pipelines is the core, Triggers adds webhook-driven execution, the Dashboard provides a UI, and tkn is the CLI. Mention the prerequisites: Kubernetes 1.25 or later, a default StorageClass for PVC-backed workspaces, and verifying that controller pods reach Running state before creating resources. If asked about production readiness, discuss resource requirements for the controller, RBAC considerations for multi-tenant clusters, and the process for upgrading Tekton versions. Showing awareness of the StorageClass prerequisite and the webhook readiness check demonstrates you have actually installed and troubleshot Tekton rather than just reading the quickstart guide.
◈ Architecture Diagram
┌──────────────────────────────────────────────┐ │ Tekton Installation Stack │ │ │ │ ┌───────────────────────────────────────┐ │ │ │ Tekton Dashboard (optional) │ │ │ │ Web UI on port 9097 │ │ │ └───────────────────┬───────────────────┘ │ │ │ │ │ ┌───────────────────┴───────────────────┐ │ │ │ Tekton Triggers (optional) │ │ │ │ EventListener, TriggerBinding, │ │ │ │ TriggerTemplate, Interceptors │ │ │ └───────────────────┬───────────────────┘ │ │ │ │ │ ┌───────────────────┴───────────────────┐ │ │ │ Tekton Pipelines (core) │ │ │ │ Controller + Webhook │ │ │ │ CRDs: Task, Pipeline, Run, etc. │ │ │ └───────────────────┬───────────────────┘ │ │ │ │ │ ┌───────────────────┴───────────────────┐ │ │ │ Kubernetes Cluster (v1.25+) │ │ │ │ + Default StorageClass │ │ │ └───────────────────────────────────────┘ │ │ │ │ Developer Tools: tkn CLI, kubectl │ └──────────────────────────────────────────────┘
💬 Comments
Quick Answer
Tekton Workspaces are declared volumes that provide shared filesystem access to steps within a Task or across Tasks in a Pipeline. They abstract the underlying storage mechanism, allowing the same Task definition to work with PersistentVolumeClaims, ConfigMaps, Secrets, or emptyDir volumes depending on what the TaskRun or PipelineRun provides.
Detailed Answer
Think of a workspace as a shared locker at a gym. The locker specification (workspace declaration) says you need a place to store your gear, but it does not specify whether the locker is a small one, a large one, or a temporary bag check. When you actually arrive (create a Run), you choose which locker to use (PVC, emptyDir, ConfigMap, or Secret). This abstraction lets the same workout routine (Task) work regardless of which locker you are assigned.
A Tekton Workspace is a volume declaration in a Task or Pipeline spec that provides a shared filesystem mount point for steps. In the Task definition, you declare the workspace with a name and an optional description, and then reference it in steps using the expression $(workspaces.<name>.path). When creating a TaskRun or PipelineRun, you bind the workspace to an actual volume source. The most common binding is a PersistentVolumeClaim (PVC), which provides durable storage that persists across steps and even across Tasks in a Pipeline. For example, the order-processing-service team declares a workspace called source in their build Task, and when they create a TaskRun, they bind it to a PVC that contains the cloned Git repository.
The workspace abstraction is critical because it decouples the Task definition from the storage implementation. The same git-clone Task can be used with a PVC when running in a production Pipeline (where data must persist across multiple Tasks), with an emptyDir when running a quick one-off test (where data can be discarded after the pod terminates), or with a ConfigMap when you need to inject static configuration files into the Task's filesystem. Tekton also supports volumeClaimTemplate, which automatically creates a temporary PVC for the duration of a PipelineRun and deletes it when the run completes. This is particularly useful for CI builds where you need persistent storage during the pipeline but do not want leftover PVCs cluttering the cluster.
In production workflows, the inventory-sync team uses workspaces to pass data through their deployment Pipeline. The first Task clones the repository into a PVC-backed workspace. The second Task reads the source code from that same workspace, builds a Docker image, and writes the image digest to a file in the workspace. The third Task reads the digest file and deploys the application. Without workspaces, each Task would run in its own pod with an isolated filesystem, and there would be no way to pass the built artifacts from the build Task to the deploy Task. The workspace backed by a PVC provides the shared filesystem bridge between these separate pods. Additionally, the user-auth-service team mounts a Kubernetes Secret as a read-only workspace to inject private SSH keys needed for cloning private Git repositories, keeping the credentials out of the Task definition.
A common beginner mistake is using emptyDir workspaces in a Pipeline with multiple Tasks. An emptyDir volume is scoped to a single pod, so data written by one Task in an emptyDir workspace is not visible to the next Task, which runs in a different pod. For multi-Task Pipelines, you must use a PVC or volumeClaimTemplate. Another gotcha is the access mode of the PVC: if you want multiple Tasks to access the same workspace concurrently (parallel tasks), the PVC must support ReadWriteMany (RWX) access mode, which not all storage providers support. Check your cluster's StorageClass capabilities before designing parallel workflows that share a workspace.
Code Example
# Task declaring a workspace for shared storage
apiVersion: tekton.dev/v1
# Task definition with workspace declaration
kind: Task
metadata:
name: build-inventory-sync
spec:
# Declare the workspace the Task needs
workspaces:
- name: source
description: Directory containing the application source code
- name: docker-credentials
description: Docker registry credentials for image push
optional: true
steps:
# Step reads source code from the workspace path
- name: build
image: golang:1.21
script: |
ls $(workspaces.source.path)
cd $(workspaces.source.path)
go build -o ./bin/inventory-sync ./cmd/server
# Step writes build artifact to the same workspace
- name: verify-build
image: alpine:3.18
script: |
ls -la $(workspaces.source.path)/bin/
$(workspaces.source.path)/bin/inventory-sync --version
# PipelineRun with PVC-backed workspace
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: inventory-sync-deploy-
spec:
pipelineRef:
name: inventory-sync-pipeline
workspaces:
# Bind workspace to a PVC for cross-Task data sharing
- name: shared-workspace
persistentVolumeClaim:
claimName: pipeline-ws-pvc
# Bind workspace to a Secret for credentials
- name: docker-credentials
secret:
secretName: registry-credentials
# Use volumeClaimTemplate for auto-provisioned temporary PVC
- name: temp-workspace
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1GiInterview Tip
A junior engineer typically says 'workspaces are directories for storing files.' Interviewers want to hear about the abstraction layer: workspaces decouple the Task definition from the storage implementation, so the same Task can work with PVCs, emptyDir, ConfigMaps, or Secrets depending on how the Run binds it. Explain the critical difference between emptyDir (pod-scoped, lost when the pod terminates) and PVC (persists across pods, required for multi-Task Pipelines). Mention volumeClaimTemplate for auto-provisioned temporary storage and the RWX access mode requirement for parallel tasks sharing a workspace. If asked about security, explain how Secrets can be mounted as read-only workspaces for credentials. Demonstrating awareness of the PVC access mode gotcha shows you have dealt with real storage issues in Kubernetes, which is exactly the operational experience interviewers seek.
◈ Architecture Diagram
┌──────────────────────────────────────────────┐ │ Workspace Binding Options │ │ │ │ Task Declaration Run Binding │ │ ┌──────────────┐ │ │ │ workspaces: │───→ PersistentVolumeClaim │ │ │ - name: src │ (persists across pods) │ │ │ │ │ │ │ │───→ emptyDir │ │ │ │ (pod-scoped, temporary) │ │ │ │ │ │ │ │───→ ConfigMap │ │ │ │ (read-only config files)│ │ │ │ │ │ │ │───→ Secret │ │ │ │ (credentials, keys) │ │ │ │ │ │ │ │───→ volumeClaimTemplate │ │ └──────────────┘ (auto-provisioned PVC) │ │ │ │ Pipeline with PVC Workspace: │ │ ┌────────┐ PVC ┌────────┐ PVC ┌────────┐ │ │ │ Task 1 │──────→│ Task 2 │──────→│ Task 3 │ │ │ │ clone │ shared│ build │ shared│ deploy │ │ │ └────────┘ vol └────────┘ vol └────────┘ │ └──────────────────────────────────────────────┘
💬 Comments
Quick Answer
Tekton Parameters are typed input values declared in Tasks and Pipelines that allow customization at runtime. Parameters are defined in the spec with a name, type (string or array), optional default value, and description. Values are passed when creating a TaskRun or PipelineRun, making Tasks reusable across different applications and environments.
Detailed Answer
Parameters in Tekton work like function arguments in programming. The Task definition is the function signature that declares what inputs it expects, and the TaskRun is the function call that supplies the actual values. Just as a function deploy(service_name, environment) can be called with deploy('payments-api', 'staging') or deploy('checkout-service', 'production'), a parameterized Tekton Task can build and deploy any service without modifying the Task itself.
Tekton Parameters are declared in the params section of a Task or Pipeline spec. Each parameter has a name, a type (string or array), an optional description, and an optional default value. Within the Task's steps, parameters are referenced using the syntax $(params.<name>). When you create a TaskRun, you provide values for these parameters in the params section of the TaskRun spec. If a parameter has a default value and the TaskRun does not provide one, the default is used. If a parameter has no default and no value is provided, the TaskRun creation fails with a validation error. For example, the payments-api team defines a build-image Task with parameters for git-url, git-revision, and image-tag. Each time they create a TaskRun, they supply the specific branch and tag values for that build.
Parameters flow through the Tekton resource hierarchy in a structured way. A Pipeline declares its own parameters, which are separate from the parameters of the Tasks it contains. To pass a Pipeline-level parameter down to a Task, you explicitly map it in the Pipeline's task definition using the $(params.<name>) syntax. This explicit mapping prevents naming collisions and makes the data flow visible and auditable. The order-processing-service team defines a Pipeline with parameters for git-url, target-environment, and image-registry. Inside the Pipeline, the git-url parameter is mapped to the clone Task's url parameter, and the image-registry parameter is mapped to the build Task's destination parameter. This layered approach means changing the target environment is a single parameter change in the PipelineRun, not an edit to the Pipeline or Task definitions.
In production, parameters are the key mechanism for making Tasks and Pipelines reusable. The platform engineering team at a company running the user-auth-service, inventory-sync, and checkout-service creates a single generic-build-pipeline with parameters for the repository URL, branch, image registry, deployment namespace, and replica count. Each application team creates their own PipelineRuns with application-specific parameter values, all sharing the same Pipeline definition. This reduces duplication and ensures consistency: when the platform team adds a new security scanning step to the Pipeline, all application teams automatically get it on their next run. Parameters of type array are useful for passing multiple values, such as a list of test suites to run or a list of namespaces to deploy to.
A gotcha that trips up beginners is the difference between Tekton parameters and Kubernetes environment variables. Parameters are resolved at pipeline creation time by the Tekton controller and injected into the step scripts as literal string substitutions. They are not environment variables and cannot be accessed using $VAR syntax inside the container unless you explicitly set them as env vars in the step definition. Another common mistake is trying to use parameter values computed at runtime (like the output of a shell command) as input to another parameter. Parameters are static values set at TaskRun or PipelineRun creation time. For dynamic values computed during execution, use Tekton Results instead, which allow a step to write a value that a subsequent Task can consume.
Code Example
# Task with typed parameters for reusability
apiVersion: tekton.dev/v1
# Parameterized Task that can build any application
kind: Task
metadata:
name: build-docker-image
spec:
# Declare parameters with types and defaults
params:
- name: git-url
type: string
description: Repository URL to clone
- name: git-revision
type: string
description: Branch or tag to build
default: main
- name: image-name
type: string
description: Full image name including registry
- name: build-args
type: array
description: Optional Docker build arguments
default: []
workspaces:
- name: source
steps:
# Reference parameters with $(params.name) syntax
- name: clone
image: alpine/git:latest
script: |
git clone -b $(params.git-revision) \
$(params.git-url) $(workspaces.source.path)
# Array parameters are expanded in args, not script
- name: build
image: gcr.io/kaniko-project/executor:latest
args:
- --context=$(workspaces.source.path)
- --destination=$(params.image-name)
- $(params.build-args[*])
# TaskRun supplying parameter values for payments-api
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
generateName: build-payments-api-
spec:
taskRef:
name: build-docker-image
# Pass actual values for declared parameters
params:
- name: git-url
value: https://github.com/acme/payments-api.git
- name: git-revision
value: feature/retry-logic
- name: image-name
value: registry.acme.io/payments-api:v1.4.2
- name: build-args
value:
- --build-arg=ENV=production
- --build-arg=VERSION=1.4.2
workspaces:
- name: source
emptyDir: {}Interview Tip
A junior engineer typically says 'parameters let you pass values into tasks.' Interviewers want a deeper understanding: explain the type system (string and array), default value behavior, and the $(params.name) substitution syntax. Highlight the distinction between parameters (static values set at creation time) and results (dynamic values computed during execution). Describe the parameter flow from PipelineRun to Pipeline to Task, emphasizing the explicit mapping that prevents naming collisions. If asked about best practices, mention always providing descriptions and default values for optional parameters, and using array parameters for variable-length inputs like build arguments. Showing that you understand parameters are resolved by string substitution at creation time, not as runtime environment variables, demonstrates a level of precision that distinguishes hands-on practitioners from documentation readers.
◈ Architecture Diagram
┌──────────────────────────────────────────────┐ │ Parameter Flow in Tekton │ │ │ │ ┌────────────────┐ │ │ │ PipelineRun │ │ │ │ params: │ │ │ │ git-url: ... │ │ │ │ env: staging │ │ │ └───────┬────────┘ │ │ │ passes values │ │ ↓ │ │ ┌────────────────┐ │ │ │ Pipeline │ │ │ │ params: │ │ │ │ git-url │──→ $(params.git-url) │ │ │ env │──→ $(params.env) │ │ └───────┬────────┘ │ │ │ maps to task params │ │ ↓ │ │ ┌────────────────┐ │ │ │ Task │ │ │ │ params: │ │ │ │ url │──→ step script uses │ │ │ environment │ $(params.url) │ │ └────────────────┘ │ │ │ │ ● String substitution at creation time │ │ ● NOT runtime environment variables │ │ ● Defaults used when value not provided │ └──────────────────────────────────────────────┘
💬 Comments
Quick Answer
The Tekton Dashboard is an optional web-based UI that provides a graphical interface for viewing and managing Tekton resources like Tasks, Pipelines, TaskRuns, and PipelineRuns. The Tekton CLI (tkn) is a command-line tool that simplifies interacting with Tekton resources, offering convenient commands for listing, starting, canceling, and viewing logs of pipeline executions.
Detailed Answer
If Tekton Pipelines is the engine room of a ship, the Dashboard is the bridge with its display screens showing engine status, speed, and navigation, while the tkn CLI is the walkie-talkie that lets the chief engineer bark commands from anywhere on the ship. Both provide visibility and control, but one is visual and the other is text-based. Most production teams use both: the Dashboard for at-a-glance monitoring and the CLI for scripted automation and quick troubleshooting.
The Tekton Dashboard is a Kubernetes-native web application that reads Tekton resources directly from the Kubernetes API server and presents them in a user-friendly interface. It shows a list of all Tasks, Pipelines, TaskRuns, and PipelineRuns across namespaces (if permissions allow), displays the DAG visualization of a Pipeline, shows real-time logs for running steps, and allows you to create new TaskRuns and PipelineRuns from the UI. The Dashboard is installed by applying its release YAML and is typically accessed through kubectl port-forward or an Ingress resource. For the payments-api team, the Dashboard provides a single pane of glass where a developer can see all recent pipeline runs, click into a failed run, identify which step failed, and read the error logs without ever touching kubectl.
The Tekton CLI (tkn) is a standalone binary distributed for Linux, macOS, and Windows. It communicates with the Kubernetes API server using the same kubeconfig as kubectl but provides Tekton-specific commands that are more ergonomic than raw kubectl commands. Instead of typing kubectl get pipelinerun -o yaml and parsing the YAML output to find the status, you type tkn pipelinerun list and get a clean table with names, statuses, and durations. The tkn pipeline start command is particularly useful: it creates a PipelineRun interactively, prompting you for parameter values and workspace bindings. The checkout-service team uses tkn pipeline start deploy-pipeline to kick off a deployment, and the CLI prompts them for the Git URL, branch, and target environment before creating the PipelineRun.
In production environments, the Dashboard and CLI serve complementary roles. The user-auth-service team has the Dashboard open on a shared monitor in their team area, showing the status of all pipeline runs. When a run fails, a developer uses tkn taskrun logs to stream the logs from the failed step directly to their terminal, which is faster than navigating the Dashboard UI for debugging. Platform engineers use tkn in shell scripts for automation: a nightly cron job runs tkn pipelinerun delete --keep 50 to clean up old PipelineRuns and prevent the cluster from accumulating thousands of completed run resources that consume etcd storage. The inventory-sync team uses tkn pipeline start in their deployment scripts, passing parameters programmatically rather than through the interactive prompt.
A gotcha worth noting is that the Tekton Dashboard does not provide authentication or authorization out of the box. It inherits the permissions of the service account it runs under, which by default has read access to Tekton resources in the tekton-pipelines namespace. For multi-tenant clusters where different teams should only see their own pipelines, you need to configure RBAC and potentially put the Dashboard behind an authentication proxy like OAuth2 Proxy or Dex. Another limitation is that the Dashboard does not persist logs after a pod is garbage collected by Kubernetes. For long-term log retention, teams integrate Tekton with external logging systems like Elasticsearch or Loki. The tkn CLI shares these limitations since it reads the same data from the Kubernetes API server.
Code Example
# Install the Tekton Dashboard on the cluster kubectl apply --filename https://storage.googleapis.com/tekton-releases/dashboard/latest/release.yaml # Access the Dashboard via port-forward kubectl port-forward -n tekton-pipelines svc/tekton-dashboard 9097:9097 # Install tkn CLI on macOS using Homebrew brew install tektoncd-cli # Install tkn CLI on Linux using package manager # sudo apt-get install tektoncd-cli # List all Pipelines in the current namespace tkn pipeline list # Start a Pipeline interactively with prompts for params tkn pipeline start checkout-service-pipeline # Start a Pipeline with parameters passed inline tkn pipeline start checkout-service-pipeline \ --param git-url=https://github.com/acme/checkout-service.git \ --param image-name=registry.acme.io/checkout-service:v2.1.0 \ --workspace name=shared-workspace,claimName=pipeline-pvc # List recent PipelineRuns with status tkn pipelinerun list # View logs for a specific PipelineRun tkn pipelinerun logs checkout-service-run-x7k9m # View logs for a specific TaskRun step tkn taskrun logs build-and-test-run-4fn2p --step run-tests # Cancel a running PipelineRun tkn pipelinerun cancel checkout-service-run-x7k9m # Delete old PipelineRuns, keeping only the 20 most recent tkn pipelinerun delete --keep 20 # Describe a Pipeline to see its structure and parameters tkn pipeline describe checkout-service-pipeline
Interview Tip
A junior engineer typically says 'the Dashboard is a web UI and tkn is a command-line tool.' Interviewers want to hear about practical usage: the Dashboard provides DAG visualization and real-time log streaming, while tkn enables scripted automation, interactive pipeline starts, and quick log inspection. Mention that the Dashboard lacks built-in authentication and needs an auth proxy for multi-tenant clusters. Explain that neither the Dashboard nor tkn persists logs after pod garbage collection, so teams need external logging solutions for audit trails. If asked about day-to-day usage, describe how tkn pipelinerun delete --keep N prevents etcd bloat from accumulated run resources, and how tkn pipeline start with inline parameters integrates into deployment scripts. Showing you understand the operational concerns around authentication, log retention, and resource cleanup demonstrates production experience that goes beyond just installing the tools.
◈ Architecture Diagram
┌──────────────────────────────────────────────┐ │ Tekton Management Tools │ │ │ │ ┌───────────────────────────────────────┐ │ │ │ Tekton Dashboard (Web UI) │ │ │ │ ┌─────────┐ ┌──────────┐ ┌───────┐ │ │ │ │ │Pipelines│ │TaskRuns │ │ Logs │ │ │ │ │ │ List │ │ Status │ │Stream │ │ │ │ │ └─────────┘ └──────────┘ └───────┘ │ │ │ │ http://localhost:9097 │ │ │ └───────────────────┬───────────────────┘ │ │ │ │ │ ↓ │ │ ┌───────────────────────────────────────┐ │ │ │ Kubernetes API Server │ │ │ └───────────────────┬───────────────────┘ │ │ ↑ │ │ ┌───────────────────┴───────────────────┐ │ │ │ tkn CLI (Command Line) │ │ │ │ │ │ │ │ $ tkn pipeline list │ │ │ │ $ tkn pipelinerun logs <name> │ │ │ │ $ tkn pipeline start <name> │ │ │ │ $ tkn pipelinerun delete --keep 20 │ │ │ └───────────────────────────────────────┘ │ │ │ │ ● Both read from Kubernetes API │ │ ● Dashboard needs auth proxy for multi-tenant│ │ ● Logs lost after pod garbage collection │ └──────────────────────────────────────────────┘
💬 Comments
Quick Answer
Tekton Triggers enables event-driven pipeline execution by combining an EventListener that receives incoming webhooks, a TriggerBinding that extracts parameters from the event payload, and a TriggerTemplate that creates PipelineRun resources with those extracted values.
Detailed Answer
Think of Tekton Triggers like a mail sorting facility at a post office. The EventListener is the mailbox that receives all incoming packages. The TriggerBinding is the sorting clerk who opens each package and reads the label to determine where it goes. The TriggerTemplate is the delivery instruction sheet that creates the actual delivery route based on the label information. Together, these three components transform raw webhook events into fully configured pipeline executions without any human intervention.
The EventListener is a Kubernetes Deployment and Service that Tekton creates when you apply an EventListener resource. It exposes an HTTP endpoint, typically on port 8080, that receives incoming webhooks from sources like GitHub, GitLab, Bitbucket, or any system capable of sending HTTP POST requests. When a push event arrives from GitHub for the payments-api repository, the EventListener receives the JSON payload and passes it through an optional chain of interceptors. Interceptors can validate the webhook signature using a shared secret, filter events based on criteria like branch name or event type, overlay additional fields onto the payload, or even call external services using a webhook interceptor. The built-in GitHub interceptor validates X-Hub-Signature headers, ensuring that only legitimate events from your configured GitHub webhook trigger pipeline runs, preventing unauthorized actors from flooding your cluster with rogue builds.
The TriggerBinding is a mapping document that extracts specific fields from the incoming event payload and assigns them to named parameters. Using JSONPath-style expressions, you can pull values like the repository URL from body.repository.clone_url, the commit SHA from body.head_commit.id, the branch name from body.ref, and the committer email from body.head_commit.committer.email. These extracted parameters are passed to the TriggerTemplate, which acts as a factory for Kubernetes resources. The TriggerTemplate defines a parameterized resourcetemplates section that typically contains a PipelineRun specification. When the EventListener processes an event, it merges the TriggerBinding parameters into the TriggerTemplate, rendering a complete PipelineRun manifest that Tekton then submits to the Kubernetes API server for execution.
In production, teams running microservices like order-processing-service and inventory-sync typically deploy a single EventListener per cluster or namespace, using CEL interceptors to route different repository events to different TriggerTemplates. A CEL filter expression like body.repository.name == 'order-processing-service' && body.ref == 'refs/heads/main' ensures that only main branch pushes to the order processing repository trigger the production deployment pipeline. For the inventory-sync service, a separate trigger within the same EventListener routes its events to a different pipeline that includes database migration steps. This fan-out architecture avoids deploying dozens of EventListeners while maintaining clean separation of pipeline logic per service.
A critical production gotcha is securing the EventListener endpoint. Without webhook secret validation, anyone who discovers the endpoint URL can trigger pipeline runs by sending crafted payloads. Always configure the GitHub interceptor with a secretRef pointing to a Kubernetes Secret containing the webhook secret token. Additionally, EventListeners consume cluster resources proportional to incoming event volume. For high-traffic repositories with frequent pushes, configure horizontal pod autoscaling on the EventListener Deployment and set resource requests and limits appropriately. Monitor the EventListener pods for memory pressure, as large payloads from monorepo webhooks can cause OOM kills if memory limits are set too low. Use the tekton-triggers-controller logs and the EventListener sink logs to debug event processing failures, and always test your JSONPath expressions against real webhook payloads before deploying to production.
Code Example
# Create a Secret to store the GitHub webhook token
kubectl create secret generic github-webhook-secret \
--from-literal=secretToken=my-super-secret-token \
--namespace tekton-pipelines
---
# eventlistener.yaml - receives GitHub webhook events
apiVersion: triggers.tekton.dev/v1beta1 # Tekton Triggers API version
kind: EventListener # resource type for receiving webhooks
metadata: # resource metadata
name: payments-api-listener # name of the event listener
namespace: tekton-pipelines # namespace for tekton resources
spec: # event listener specification
serviceAccountName: tekton-triggers-sa # service account with RBAC permissions
triggers: # list of triggers to evaluate
- name: github-push-trigger # descriptive trigger name
interceptors: # chain of interceptors for validation
- ref: # reference to built-in interceptor
name: "github" # GitHub-specific interceptor
params: # interceptor parameters
- name: "secretRef" # reference to webhook secret
value: # secret location details
secretName: github-webhook-secret # k8s secret name
secretKey: secretToken # key within the secret
- name: "eventTypes" # filter by event type
value: ["push"] # only process push events
- ref: # second interceptor in chain
name: "cel" # CEL expression interceptor
params: # CEL parameters
- name: "filter" # filter expression
value: "body.ref == 'refs/heads/main'" # only main branch pushes
bindings: # trigger bindings to extract data
- ref: payments-api-binding # reference to TriggerBinding
template: # trigger template to create resources
ref: payments-api-template # reference to TriggerTemplate
---
# triggerbinding.yaml - extracts values from webhook payload
apiVersion: triggers.tekton.dev/v1beta1 # Tekton Triggers API version
kind: TriggerBinding # resource type for parameter extraction
metadata: # resource metadata
name: payments-api-binding # binding name matching EventListener ref
namespace: tekton-pipelines # same namespace as EventListener
spec: # binding specification
params: # parameters to extract from payload
- name: gitrevision # parameter name for commit SHA
value: $(body.head_commit.id) # JSONPath to commit SHA in payload
- name: gitrepositoryurl # parameter name for repo URL
value: $(body.repository.clone_url) # JSONPath to clone URL in payload
- name: branchname # parameter name for branch
value: $(body.ref) # JSONPath to git ref in payload
---
# triggertemplate.yaml - creates PipelineRun from extracted params
apiVersion: triggers.tekton.dev/v1beta1 # Tekton Triggers API version
kind: TriggerTemplate # resource type for PipelineRun factory
metadata: # resource metadata
name: payments-api-template # template name matching EventListener ref
namespace: tekton-pipelines # same namespace as EventListener
spec: # template specification
params: # parameters received from TriggerBinding
- name: gitrevision # commit SHA parameter
description: The git revision # parameter description
- name: gitrepositoryurl # repository URL parameter
description: The git repository url # parameter description
- name: branchname # branch name parameter
description: The git branch ref # parameter description
resourcetemplates: # resources to create on trigger
- apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: PipelineRun # create a PipelineRun resource
metadata: # PipelineRun metadata
generateName: payments-api-run- # auto-generate unique run name
spec: # PipelineRun specification
pipelineRef: # reference to existing Pipeline
name: payments-api-pipeline # Pipeline name to execute
params: # parameters passed to the Pipeline
- name: repo-url # pipeline param for repository URL
value: $(tt.params.gitrepositoryurl) # value from TriggerTemplate param
- name: revision # pipeline param for commit SHA
value: $(tt.params.gitrevision) # value from TriggerTemplate param
workspaces: # workspace bindings for the pipeline
- name: shared-workspace # workspace name defined in Pipeline
volumeClaimTemplate: # dynamically provision PVC
spec: # PVC specification
accessModes: # PVC access modes
- ReadWriteOnce # single node read-write access
resources: # PVC resource requirements
requests: # storage request
storage: 1Gi # 1 GiB of storage for source codeInterview Tip
A junior engineer typically describes Tekton Triggers as simply catching webhooks, but interviewers want to see you understand the full data flow from event ingestion to PipelineRun creation. Walk through how the EventListener receives the HTTP POST, how interceptors validate the signature and filter by branch or event type, how the TriggerBinding uses JSONPath expressions to extract specific fields from the payload body, and how the TriggerTemplate acts as a factory that stamps out PipelineRun resources with those extracted values. Mention the CEL interceptor as the production-grade way to implement complex routing logic within a single EventListener. If you can explain how you secured the endpoint with webhook secret validation and monitored EventListener pod health in a real project, you demonstrate operational maturity that separates you from candidates who only followed tutorials.
◈ Architecture Diagram
┌─────────────────────────────────────────────────────────┐
│ Tekton Triggers Architecture │
└────────────────────────────┬────────────────────────────┘
│
┌───────────────────────┴───────────────────────┐
│ GitHub / GitLab Webhook │
│ POST /webhook (JSON payload) │
└───────────────────────┬───────────────────────┘
│
↓
┌───────────────────────────────────────────────┐
│ EventListener │
│ ┌─────────────────────────────────────────┐ │
│ │ Interceptor Chain: │ │
│ │ ● GitHub → validate X-Hub-Signature │ │
│ │ ● CEL → filter branch == main │ │
│ └──────────────────┬──────────────────────┘ │
└─────────────────────┬─────────────────────────┘
│ validated event
┌────────────┴────────────┐
↓ ↓
┌──────────────────────┐ ┌──────────────────────────┐
│ TriggerBinding │ │ TriggerTemplate │
│ ● gitrevision │→ │ resourcetemplates: │
│ ● gitrepositoryurl │ │ kind: PipelineRun │
│ ● branchname │ │ params: $(tt.params) │
└──────────────────────┘ └────────────┬─────────────┘
│
↓
┌──────────────────────────┐
│ PipelineRun Created │
│ payments-api-run-x7k2m │
│ ● clone → build → push │
└──────────────────────────┘💬 Comments
Quick Answer
Tekton Results allow a Task to emit string values by writing to files under the /tekton/results directory, which downstream Tasks can consume as parameter values using the $(tasks.taskname.results.resultname) variable substitution syntax within a Pipeline.
Detailed Answer
Think of Tekton Results like sticky notes that workers leave on a shared bulletin board in a factory assembly line. Each worker completes their station, writes a note with critical information like a part number or quality measurement, and pins it to the board. The next worker downstream reads those notes before starting their own job. In Tekton, Results are the mechanism that lets one Task produce output data that subsequent Tasks can consume as input, enabling dynamic data flow across an otherwise statically defined Pipeline.
At the Task level, you declare Results in the spec.results section with a name and an optional description. During execution, the Task step writes a value to a file at the path /tekton/results/<result-name>. The Tekton controller reads this file after the step container completes and stores the value as part of the TaskRun status. The value must be a string and, importantly, is subject to a size limit. In Tekton Pipelines v0.44 and later, individual results can be up to 4096 bytes by default, though this limit is configurable via the feature-flags ConfigMap. For larger data, you should write to a workspace volume and pass a file path as the result instead of the data itself. Results are terminated at the exact byte boundary of the file content, so avoid trailing newlines unless they are intentional, as they become part of the stored value.
Within a Pipeline, you reference a Task result using the variable substitution syntax $(tasks.<task-name>.results.<result-name>). This creates an implicit ordering dependency: the consuming Task will not start until the producing Task has completed successfully. For example, in a payments-api pipeline, a build-image Task can emit the full image digest as a result, and the subsequent deploy Task consumes that digest to ensure it deploys the exact image that was just built, not a mutable tag that could have been overwritten. This pattern eliminates the race condition where a deploy step pulls a latest tag that points to a different build than the one just tested. The Pipeline can also emit its own results by mapping Task results to Pipeline-level results in spec.results, making them accessible to external systems querying the PipelineRun status.
In production microservice workflows, Results become the glue connecting stages of complex pipelines. Consider a pipeline for the order-processing-service that first runs a generate-version Task to compute the semantic version from git tags, producing a result called version. The next Task, build-and-push, consumes that version to tag the container image and emits a result called image-digest containing the SHA256 digest. A parallel security-scan Task consumes the image-digest to scan the exact image for vulnerabilities and emits a scan-status result of either pass or fail. The deploy Task uses a when expression to check if scan-status equals pass before proceeding with the deployment, consuming the image-digest result to update the Kubernetes Deployment manifest with the immutable digest reference. This chain of Results creates a verifiable audit trail where every artifact is traceable from source commit to running container.
A common gotcha is attempting to use Results for large data transfers like test reports, coverage files, or build logs. Results are designed for small metadata values such as image digests, version strings, commit SHAs, or status flags. Writing megabytes of data to /tekton/results will either be truncated or cause the TaskRun to fail with a result size exceeded error. For large artifacts, use a shared workspace backed by a PersistentVolumeClaim where one Task writes files and the next Task reads them from the same mounted volume. Another pitfall is forgetting that Results create implicit dependencies. If Task B references a result from Task A, Task B cannot run in parallel with Task A even if you intended them to be parallel. Always review the Pipeline execution graph after adding result references to verify that the parallelism and ordering match your expectations. Use tkn pipelinerun describe to inspect result values after execution for debugging.
Code Example
# Task that produces results - generates image digest after build
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Task # Task resource definition
metadata: # resource metadata
name: build-and-push # task name for pipeline reference
spec: # task specification
params: # input parameters
- name: image-name # container image name parameter
type: string # string type parameter
description: Full image name to build # parameter description
results: # output results declaration
- name: image-digest # result name for downstream tasks
description: SHA256 digest of built image # what the result contains
- name: image-url # result name for full image URL
description: Full image URL with digest # what the result contains
steps: # task execution steps
- name: build-image # step that builds the container
image: gcr.io/kaniko-project/executor:v1.19.2 # kaniko builder image
args: # kaniko arguments
- --destination=$(params.image-name) # push to specified registry
- --digest-file=/tekton/results/image-digest # write digest to results path
- name: store-image-url # step that computes full image URL
image: alpine:3.18 # lightweight alpine image
script: | # inline script block
#!/bin/sh # shell interpreter
DIGEST=$(cat /tekton/results/image-digest) # read digest from previous step
echo -n "$(params.image-name)@${DIGEST}" > /tekton/results/image-url # write full URL to results
---
# Task that consumes results - deploys the built image
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Task # Task resource definition
metadata: # resource metadata
name: deploy-to-kubernetes # task name for pipeline reference
spec: # task specification
params: # input parameters
- name: image-digest # digest received from build task
type: string # string type parameter
- name: image-url # full image URL from build task
type: string # string type parameter
- name: namespace # target deployment namespace
type: string # string type parameter
steps: # task execution steps
- name: patch-deployment # step to update the deployment
image: bitnami/kubectl:1.28 # kubectl image for cluster access
script: | # inline script block
#!/bin/sh # shell interpreter
echo "Deploying image: $(params.image-url)" # log the exact image being deployed
kubectl set image deployment/payments-api \ # update deployment image
payments-api=$(params.image-url) \ # set container image to digest URL
--namespace=$(params.namespace) # in the specified namespace
kubectl rollout status deployment/payments-api \ # wait for rollout completion
--namespace=$(params.namespace) \ # in the specified namespace
--timeout=300s # timeout after 5 minutes
---
# Pipeline wiring results between tasks
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Pipeline # Pipeline resource definition
metadata: # resource metadata
name: payments-api-pipeline # pipeline name
spec: # pipeline specification
results: # pipeline-level results
- name: deployed-digest # expose digest at pipeline level
value: $(tasks.build.results.image-digest) # map from task result
tasks: # pipeline task definitions
- name: build # task reference name in pipeline
taskRef: # reference to Task resource
name: build-and-push # the build task defined above
params: # parameters passed to the task
- name: image-name # image name parameter
value: registry.acme.io/payments-api # target registry and image
- name: deploy # task reference name in pipeline
taskRef: # reference to Task resource
name: deploy-to-kubernetes # the deploy task defined above
runAfter: # explicit ordering (also implied by results)
- build # wait for build to complete
params: # parameters passed to the task
- name: image-digest # pass digest from build task
value: $(tasks.build.results.image-digest) # reference build task result
- name: image-url # pass full URL from build task
value: $(tasks.build.results.image-url) # reference build task result
- name: namespace # target namespace for deployment
value: production # deploy to production namespaceInterview Tip
A junior engineer typically confuses Tekton Results with workspaces or tries to pass large files through Results. Interviewers want you to clearly distinguish between Results for small metadata strings like image digests, version numbers, and status flags versus workspaces for large file transfers like source code, build artifacts, and test reports. Explain the /tekton/results file path mechanism and the 4096-byte default size limit. Show that you understand how result references create implicit task ordering dependencies in the Pipeline DAG. Mention the $(tasks.taskname.results.resultname) syntax confidently, and explain how Pipeline-level results expose task outputs to external systems. If you can describe a real scenario where you used an image digest result to ensure deploy-time immutability in a payments-api pipeline, that demonstrates production thinking that impresses interviewers.
◈ Architecture Diagram
┌─────────────────────────────────────────────────────┐
│ Tekton Results Data Flow │
└──────────────────────┬──────────────────────────────┘
│
┌────────────────────┴────────────────────┐
│ Pipeline: payments-api │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Task: build-and-push │ │
│ │ ┌────────────────────────────┐ │ │
│ │ │ Step: build-image │ │ │
│ │ │ writes → /tekton/results/ │ │ │
│ │ │ image-digest: sha256:... │ │ │
│ │ └────────────┬───────────────┘ │ │
│ │ ┌────────────↓───────────────┐ │ │
│ │ │ Step: store-image-url │ │ │
│ │ │ reads digest → writes url │ │ │
│ │ │ image-url: reg/app@sha.. │ │ │
│ │ └────────────────────────────┘ │ │
│ │ results: │ │
│ │ ● image-digest ✓ │ │
│ │ ● image-url ✓ │ │
│ └───────────────┬──────────────────┘ │
│ │ │
│ $(tasks.build.results.image-digest) │
│ $(tasks.build.results.image-url) │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────┐ │
│ │ Task: deploy-to-kubernetes │ │
│ │ params: │ │
│ │ ● image-digest → from build │ │
│ │ ● image-url → from build │ │
│ │ ┌────────────────────────────┐ │ │
│ │ │ Step: patch-deployment │ │ │
│ │ │ kubectl set image → digest │ │ │
│ │ └────────────────────────────┘ │ │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────┘💬 Comments
Quick Answer
When Expressions in Tekton allow you to conditionally skip Tasks in a Pipeline by evaluating an input value against a set of allowed values using the In or NotIn operators, enabling branching logic such as skipping deployment when tests fail or running specific tasks only for certain branches.
Detailed Answer
Think of When Expressions like a bouncer at a club entrance. Before each Task is allowed to execute, the bouncer checks the guest list. If the Task passes the check, it enters and runs. If it fails the check, it is politely turned away and marked as skipped rather than failed. This skip behavior is important because it means the overall Pipeline can still succeed even when some Tasks are conditionally skipped, unlike a failed Task which would cause the Pipeline to fail.
When Expressions are defined at the Task level within a Pipeline using the when field. Each when expression consists of three parts: an input value that can be a static string or a variable reference, an operator which is either In or NotIn, and a values array containing the allowed or disallowed strings to compare against. The input is evaluated against the values list using the specified operator. If all when expressions on a Task evaluate to true, the Task executes. If any single when expression evaluates to false, the entire Task is skipped. The input field commonly references Pipeline parameters using $(params.paramname), Task results using $(tasks.taskname.results.resultname), or context variables like $(context.pipeline.name). This makes when expressions dynamic and responsive to runtime values rather than being hardcoded at definition time.
In production pipelines, when expressions enable sophisticated branching patterns without requiring separate Pipeline definitions for each workflow variation. Consider a CI/CD pipeline for the checkout-service where the team wants to run integration tests only on pull request branches but skip them on feature branches to save cluster resources. A when expression with input $(params.branch) operator In values ["refs/heads/main", "refs/heads/release/*"] on the integration-test Task ensures that only main and release branches get the full test suite. For the deploy-to-staging Task, a when expression checks that the preceding security-scan Task produced a result of pass using input $(tasks.security-scan.results.status) operator In values ["pass"]. If the scan found critical vulnerabilities and reported fail, the deployment Task is skipped entirely, preventing vulnerable code from reaching any environment.
The behavior of downstream Tasks when an upstream Task is skipped deserves special attention. In Tekton v0.44 and later, you can configure the scope-when-expressions-to-task feature flag to control whether a skipped Task causes its downstream dependents to also be skipped. With this flag set to true, only the Task with the failing when expression is skipped, and downstream Tasks that depend on it via runAfter but do not reference its results will still execute. Without this flag, the default behavior cascades the skip to all downstream Tasks, effectively creating a short-circuit in the Pipeline. Understanding this distinction is crucial for designing pipelines where some branches should continue even when a conditional Task is skipped. For instance, in an order-processing-service pipeline, you might want the notification Task to run regardless of whether the optional performance-test Task was skipped.
A common mistake is trying to use when expressions for complex boolean logic with AND and OR combinations. When expressions only support simple equality checks with In and NotIn operators. If you need to evaluate whether a branch is main AND the test result is pass, you add two separate when expressions to the same Task, because multiple when expressions are evaluated with AND logic. For OR logic, you must restructure your Pipeline or use a preceding Task that evaluates the complex condition and writes a single result that the when expression can check. Another gotcha is that skipped Tasks do not produce results, so any downstream Task referencing results from a potentially skipped Task will fail with a missing result error. Always design your Pipeline DAG so that Tasks consuming results only depend on Tasks that are guaranteed to execute. Use the tkn pipelinerun describe command to inspect which Tasks were skipped and why, which is invaluable for debugging conditional execution flows in production.
Code Example
# Pipeline with When Expressions for conditional execution
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Pipeline # Pipeline resource definition
metadata: # resource metadata
name: checkout-service-pipeline # pipeline name
spec: # pipeline specification
params: # pipeline-level parameters
- name: branch # git branch parameter
type: string # string type
- name: run-integration-tests # toggle for integration tests
type: string # string type
default: "true" # enabled by default
workspaces: # pipeline workspace declarations
- name: source # shared source code workspace
tasks: # pipeline task list
- name: clone-source # first task clones the repo
taskRef: # reference to catalog task
name: git-clone # Tekton Hub git-clone task
workspaces: # workspace bindings
- name: output # git-clone output workspace
workspace: source # bind to pipeline workspace
params: # task parameters
- name: url # repository URL
value: https://github.com/acme/checkout-service # clone URL
- name: revision # git revision to checkout
value: $(params.branch) # use pipeline branch param
- name: unit-tests # run unit tests always
taskRef: # reference to test task
name: run-tests # task that executes tests
runAfter: # ordering dependency
- clone-source # wait for clone to complete
workspaces: # workspace bindings
- name: source # source code workspace
workspace: source # bind to pipeline workspace
- name: integration-tests # conditional integration tests
when: # when expressions for this task
- input: $(params.run-integration-tests) # check the toggle parameter
operator: In # must be in the allowed values
values: ["true"] # only run when explicitly enabled
- input: $(params.branch) # also check the branch
operator: In # must match allowed branches
values: ["refs/heads/main", "refs/heads/develop"] # main or develop only
taskRef: # reference to integration test task
name: run-integration-tests # task for integration testing
runAfter: # ordering dependency
- unit-tests # wait for unit tests to pass
workspaces: # workspace bindings
- name: source # source code workspace
workspace: source # bind to pipeline workspace
- name: security-scan # scan for vulnerabilities
taskRef: # reference to scan task
name: trivy-scan # Trivy security scanner
runAfter: # ordering dependency
- unit-tests # run after unit tests
workspaces: # workspace bindings
- name: source # source code workspace
workspace: source # bind to pipeline workspace
- name: deploy-staging # conditional deployment task
when: # when expressions for deployment
- input: $(tasks.security-scan.results.scan-status) # check scan result
operator: In # must be in allowed values
values: ["pass"] # only deploy if scan passed
- input: $(params.branch) # check branch name
operator: NotIn # must NOT be in blocked list
values: ["refs/heads/experimental"] # skip deploy for experimental
taskRef: # reference to deploy task
name: deploy-to-kubernetes # kubernetes deployment task
runAfter: # ordering dependencies
- security-scan # wait for scan completion
params: # task parameters
- name: environment # target environment
value: staging # deploy to staging
- name: notify-team # always notify regardless of skips
taskRef: # reference to notification task
name: send-slack-notification # Slack notification task
runAfter: # ordering dependency
- deploy-staging # run after deploy attempt
params: # notification parameters
- name: message # message content
value: "Pipeline completed for $(params.branch)" # dynamic messageInterview Tip
A junior engineer typically confuses when expressions with traditional if-else branching and tries to implement complex boolean logic that when expressions do not support. Interviewers want you to explain the three components clearly: input, operator (In or NotIn), and values array. Emphasize that multiple when expressions on the same Task use AND logic, and that OR logic requires a workaround using a preceding evaluator Task. Explain the critical distinction between a skipped Task and a failed Task, specifically that skipped Tasks do not cause Pipeline failure. Mention the scope-when-expressions-to-task feature flag and how it controls whether downstream Tasks are cascaded as skipped or allowed to continue. If you can describe a production scenario where you used a security scan result to gate deployment with a when expression, that demonstrates practical pipeline design thinking.
◈ Architecture Diagram
┌─────────────────────────────────────────────────────┐
│ When Expression Evaluation Flow │
└──────────────────────┬──────────────────────────────┘
│
┌────────────────────┴────────────────────┐
│ Pipeline: checkout-service │
│ │
│ ┌──────────────────────────────────┐ │
│ │ clone-source │ │
│ │ (always runs) │ │
│ └───────────────┬──────────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────┐ │
│ │ unit-tests │ │
│ │ (always runs) │ │
│ └──────┬───────────────┬───────────┘ │
│ │ │ │
│ ↓ ↓ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ integration │ │ security-scan │ │
│ │ tests │ │ (always runs) │ │
│ │ │ │ │ │
│ │ when: │ │ results: │ │
│ │ branch In │ │ scan-status │ │
│ │ [main,dev] │ │ │ │
│ │ │ │ │ │
│ │ ✓ → runs │ │ │ │
│ │ ✗ → skipped │ │ │ │
│ └──────────────┘ └────────┬────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────────────┐ │
│ │ deploy-staging │ │
│ │ │ │
│ │ when: │ │
│ │ scan-status In │ │
│ │ [pass] │ │
│ │ │ │
│ │ ✓ pass → deploys │ │
│ │ ✗ fail → skipped │ │
│ └─────────┬───────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────────────┐ │
│ │ notify-team │ │
│ │ (always runs) │ │
│ └─────────────────────┘ │
└────────────────────────────────────────┘💬 Comments
Quick Answer
Tekton Pipelines access secrets through Kubernetes Secret resources mounted as environment variables or volume files in Task steps, with service account annotations providing automatic Git and Docker registry authentication, and external secret managers like HashiCorp Vault injecting credentials at runtime.
Detailed Answer
Think of managing secrets in Tekton like handling keys in a large hotel. The front desk has a master key cabinet that guests never access directly. Instead, the concierge hands each guest only the specific room key they need, and the key is collected when they check out. In Tekton, Kubernetes Secrets are the key cabinet, service accounts are the concierge, and Task steps are the guests who receive only the credentials they need for the duration of their execution.
The most fundamental approach is creating Kubernetes Secret resources and referencing them in Task step definitions. You can mount secrets as environment variables using the envFrom or env with valueFrom.secretKeyRef syntax, or as files using volume mounts with a secret volume type. For the payments-api pipeline, a database migration step needs the DATABASE_URL connection string. Rather than hardcoding it in the Pipeline definition, you create a Secret containing the connection string and reference it in the step's env block. The step container sees the credential as a normal environment variable, and the Secret value never appears in Pipeline YAML, TaskRun logs, or Tekton dashboard displays. For credentials that must be files, such as GCP service account JSON keys or TLS certificates, volume-mounting the Secret to a specific path like /etc/secrets/sa-key.json gives the step direct file access while keeping the credential managed through Kubernetes RBAC.
Tekton provides a specialized mechanism for Git and Docker registry authentication through annotated Secrets linked to service accounts. By creating a Secret of type kubernetes.io/basic-auth with a tekton.dev/git-0 annotation pointing to the Git server URL, and attaching it to the service account used by the TaskRun, Tekton automatically configures Git credential helpers inside every step container. The same pattern works for Docker registries using kubernetes.io/dockerconfigjson secrets with tekton.dev/docker-0 annotations. When the user-auth-service pipeline clones a private repository and pushes images to a private registry, the service account carries both the Git credentials for cloning and the Docker credentials for pushing, without any explicit Secret references in the Task definition. This declarative approach centralizes credential management at the service account level and decouples Task definitions from specific credential implementations.
For enterprise environments, integrating external secret managers provides dynamic credential rotation and fine-grained audit trails that Kubernetes Secrets alone cannot offer. HashiCorp Vault integration typically uses the Vault Agent sidecar injector, which adds init containers and sidecar containers to TaskRun pods that fetch secrets from Vault and write them to a shared in-memory volume. The External Secrets Operator is another popular approach that synchronizes secrets from AWS Secrets Manager, Azure Key Vault, or GCP Secret Manager into Kubernetes Secret resources, which Tekton Tasks then consume through standard volume mounts or environment variables. For the inventory-sync service that connects to third-party supplier APIs with rotating API keys, the External Secrets Operator ensures that the Kubernetes Secret always contains the current API key, and Tekton Tasks pick up the fresh credentials on every run without pipeline changes.
Security best practices demand treating Tekton secret management as a defense-in-depth problem. Never echo secret values in step scripts, as TaskRun logs are accessible through kubectl and the Tekton dashboard. Use Kubernetes RBAC to restrict which service accounts can access which Secrets, applying the principle of least privilege so that the checkout-service pipeline cannot read Secrets belonging to the payments-api pipeline. Enable encryption at rest for etcd to protect Secret values stored in the cluster. Rotate credentials regularly and use short-lived tokens where possible, such as OIDC tokens for cloud provider authentication instead of long-lived service account keys. Tekton Chains can sign TaskRun and PipelineRun attestations, providing a verifiable record that specific credentials were used during specific pipeline executions, supporting compliance requirements for SOC 2 and PCI DSS environments.
Code Example
# Create a Kubernetes Secret for database credentials
kubectl create secret generic payments-db-credentials \
--from-literal=DB_HOST=payments-db.prod.svc.cluster.local \
--from-literal=DB_USER=payments_app \
--from-literal=DB_PASSWORD=s3cur3-pa55w0rd \
--from-literal=DB_NAME=payments_production \
--namespace tekton-pipelines
---
# Create a Docker registry Secret for image pushing
kubectl create secret docker-registry acme-registry-creds \
--docker-server=registry.acme.io \
--docker-username=tekton-builder \
--docker-password=registry-token-value \
--namespace tekton-pipelines
---
# Create a Git SSH Secret for private repo cloning
kubectl create secret generic git-ssh-credentials \
--from-file=ssh-privatekey=$HOME/.ssh/id_ed25519 \
--from-file=known_hosts=$HOME/.ssh/known_hosts \
--type=kubernetes.io/ssh-auth \
--namespace tekton-pipelines
---
# Annotate secrets for Tekton auto-injection
kubectl annotate secret git-ssh-credentials \
tekton.dev/git-0=github.com \
--namespace tekton-pipelines
---
# Annotate Docker secret for Tekton auto-injection
kubectl annotate secret acme-registry-creds \
tekton.dev/docker-0=registry.acme.io \
--namespace tekton-pipelines
---
# ServiceAccount linking secrets to pipeline runs
apiVersion: v1 # core API version
kind: ServiceAccount # ServiceAccount resource
metadata: # resource metadata
name: payments-pipeline-sa # service account name
namespace: tekton-pipelines # namespace for tekton
secrets: # secrets available to this SA
- name: acme-registry-creds # docker registry credentials
- name: git-ssh-credentials # git SSH key for cloning
- name: payments-db-credentials # database connection secrets
---
# Task that consumes secrets via environment variables
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Task # Task resource definition
metadata: # resource metadata
name: run-db-migration # task name
spec: # task specification
steps: # execution steps
- name: migrate # migration step name
image: registry.acme.io/payments-api:latest # application image
command: ["python"] # python entrypoint
args: ["manage.py", "migrate"] # run database migrations
env: # environment variables from secrets
- name: DB_HOST # database host variable
valueFrom: # value sourced externally
secretKeyRef: # from a Kubernetes Secret
name: payments-db-credentials # secret resource name
key: DB_HOST # key within the secret
- name: DB_USER # database user variable
valueFrom: # value sourced externally
secretKeyRef: # from a Kubernetes Secret
name: payments-db-credentials # secret resource name
key: DB_USER # key within the secret
- name: DB_PASSWORD # database password variable
valueFrom: # value sourced externally
secretKeyRef: # from a Kubernetes Secret
name: payments-db-credentials # secret resource name
key: DB_PASSWORD # key within the secret
---
# Task that mounts secrets as files (e.g., GCP service account)
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Task # Task resource definition
metadata: # resource metadata
name: deploy-with-gcp-creds # task name
spec: # task specification
steps: # execution steps
- name: deploy # deployment step
image: google/cloud-sdk:slim # GCP SDK image
script: | # inline script
#!/bin/bash # bash interpreter
gcloud auth activate-service-account \ # activate GCP service account
--key-file=/etc/secrets/gcp-sa.json # using mounted key file
gcloud run deploy inventory-sync \ # deploy to Cloud Run
--image=gcr.io/acme/inventory-sync # specify container image
volumeMounts: # mount volumes into container
- name: gcp-credentials # volume name reference
mountPath: /etc/secrets # mount path in container
readOnly: true # read-only for security
volumes: # volume definitions
- name: gcp-credentials # volume name
secret: # source from Kubernetes Secret
secretName: gcp-service-account-key # secret containing the JSON keyInterview Tip
A junior engineer typically hardcodes credentials in Pipeline YAML or mounts every secret as an environment variable without considering security implications. Interviewers want to see a layered approach to secret management. Start by explaining Kubernetes Secret resources with RBAC restrictions, then discuss Tekton-specific annotations on Secrets for automatic Git and Docker credential injection via service accounts. Progress to external secret managers like Vault or the External Secrets Operator for dynamic rotation and audit trails. Mention defense-in-depth practices: never echo secrets in scripts, encrypt etcd at rest, use short-lived OIDC tokens over static keys, and scope service accounts with least-privilege RBAC. If you can reference Tekton Chains for supply chain attestation and compliance, you demonstrate enterprise security awareness that strongly impresses interviewers.
◈ Architecture Diagram
┌─────────────────────────────────────────────────────────┐
│ Tekton Secret Management Architecture │
└────────────────────────────┬────────────────────────────┘
│
┌──────────────────────────┴──────────────────────────┐
│ Secret Sources │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ K8s Secret │ │ Vault Agent │ │ External │ │
│ │ (native) │ │ (sidecar) │ │ Secrets Op │ │
│ └──────┬───────┘ └──────┬───────┘ └─────┬──────┘ │
└─────────┼────────────────┼───────────────┼─────────┘
│ │ │
↓ ↓ ↓
┌────────────────────────────────────────────────────┐
│ ServiceAccount │
│ ┌──────────────────────────────────────────────┐ │
│ │ payments-pipeline-sa │ │
│ │ secrets: │ │
│ │ ● acme-registry-creds (docker-0) │ │
│ │ ● git-ssh-credentials (git-0) │ │
│ │ ● payments-db-credentials │ │
│ └──────────────────┬───────────────────────────┘ │
└─────────────────────┼──────────────────────────────┘
│
↓
┌────────────────────────────────────────────────────┐
│ TaskRun Pod │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Step: migrate │ │ Step: deploy │ │
│ │ env: │ │ volumeMount: │ │
│ │ DB_HOST ← secret │ │ /etc/secrets/ │ │
│ │ DB_USER ← secret │ │ gcp-sa.json │ │
│ │ DB_PASS ← secret │ │ ← secret volume │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │
│ Auto-injected by Tekton: │
│ ● Git credentials → ~/.gitconfig │
│ ● Docker credentials → ~/.docker/config.json │
└────────────────────────────────────────────────────┘💬 Comments
Quick Answer
Tekton builds and pushes container images using unprivileged builder tools like Kaniko or Buildah that run as regular containers within Task steps, reading Dockerfiles from a workspace and pushing the resulting image to a registry using credentials provided through Kubernetes Secrets.
Detailed Answer
Think of building container images in Tekton like assembling a product in a clean-room factory. Traditional Docker builds require access to the Docker daemon, which is like needing the factory's master control panel to assemble a single product. Kaniko and Buildah are self-contained assembly stations that can build the product independently without needing the master control panel, making them safe to operate in shared environments where giving every worker access to the master controls would be a security risk.
Kaniko is the most popular image builder in Tekton pipelines because it runs entirely in userspace without requiring a Docker daemon or privileged container mode. The Kaniko executor reads a Dockerfile from the workspace, executes each instruction in a sandboxed environment, and pushes the resulting image layers directly to a container registry. In a Tekton Task, you create a step using the gcr.io/kaniko-project/executor image, pass the Dockerfile location and build context via arguments, and specify the destination registry and tag with the --destination flag. Kaniko supports multi-stage builds, build arguments, layer caching through a remote cache repository, and digest file output that integrates perfectly with Tekton Results. For the payments-api service, Kaniko reads the Dockerfile from the cloned source workspace, builds the Go binary in a builder stage, copies it into a distroless runtime image, and pushes the result to registry.acme.io/payments-api with both a git-sha tag and a semver tag.
Buildah offers an alternative approach that provides more flexibility and closer compatibility with Docker build behavior. Buildah supports Dockerfile-based builds through the buildah bud command, but also supports building images programmatically using buildah from, buildah copy, buildah run, and buildah commit commands. This programmatic approach is valuable when you need to construct images based on dynamic conditions that are difficult to express in a Dockerfile. Buildah can run without root privileges using the --storage-driver=vfs flag, though it typically needs some elevated capabilities. In Tekton, Buildah steps use the quay.io/buildah/buildah image and mount the container storage as an emptyDir volume. For the user-auth-service, a Buildah-based Task can conditionally include debug tools in development builds while producing minimal images for production, all within the same Task definition using script logic.
Registry authentication for both Kaniko and Buildah is handled through Kubernetes Secrets. Kaniko reads Docker credentials from a config.json file, which you provide either through Tekton's automatic credential injection via annotated Secrets on the service account, or by explicitly mounting a dockerconfigjson Secret to /kaniko/.docker/config.json. Buildah uses the --authfile flag pointing to a mounted credentials file. For multi-registry workflows where the checkout-service pipeline pulls base images from Docker Hub, caches layers in a GCR cache repository, and pushes the final image to a private ECR registry, you create separate Secrets for each registry and mount them into the builder step. Kaniko supports multiple --destination flags to push the same image to multiple registries in a single build, which is useful for disaster recovery setups where images must exist in two geographically separated registries.
Performance optimization is critical for production Tekton image builds. Kaniko's --cache=true flag enables layer caching using a remote registry as the cache backend, dramatically reducing build times for images where early layers like dependency installation rarely change. The --cache-repo flag specifies where cached layers are stored. For the order-processing-service with a large node_modules layer, enabling caching reduces build times from eight minutes to under two minutes when only application code changes. Buildah benefits from mounting a persistent cache volume for the package manager cache directory. Both tools support --snapshot-mode=redo in Kaniko and equivalent optimizations in Buildah to skip unchanged layers more efficiently. Always output the image digest to /tekton/results so downstream Tasks can reference the exact immutable image rather than a mutable tag that could be overwritten by a concurrent build.
Code Example
# Task using Kaniko to build and push container images
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Task # Task resource definition
metadata: # resource metadata
name: kaniko-build # task name for pipeline reference
spec: # task specification
params: # input parameters
- name: image # target image name parameter
type: string # string type
description: Full image name with registry # parameter description
- name: dockerfile # Dockerfile path parameter
type: string # string type
default: ./Dockerfile # default Dockerfile location
- name: context # build context directory
type: string # string type
default: . # default to workspace root
workspaces: # workspace declarations
- name: source # source code workspace
description: Source code with Dockerfile # workspace description
- name: docker-credentials # registry credentials workspace
description: Docker config.json for auth # workspace description
results: # task results
- name: image-digest # output image digest
description: SHA256 digest of pushed image # result description
- name: image-url # output full image reference
description: Full image URL with digest # result description
steps: # execution steps
- name: build-and-push # kaniko build step
image: gcr.io/kaniko-project/executor:v1.19.2 # kaniko executor image
args: # kaniko command arguments
- --dockerfile=$(params.dockerfile) # path to Dockerfile
- --context=$(workspaces.source.path)/$(params.context) # build context path
- --destination=$(params.image) # push destination with tag
- --cache=true # enable remote layer caching
- --cache-repo=$(params.image)-cache # cache repository location
- --digest-file=$(results.image-digest.path) # write digest to results
- --snapshot-mode=redo # optimized snapshot mode
- --push-retry=3 # retry push on transient failures
env: # environment variables
- name: DOCKER_CONFIG # docker config directory
value: $(workspaces.docker-credentials.path) # mounted credentials path
---
# Task using Buildah as an alternative builder
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Task # Task resource definition
metadata: # resource metadata
name: buildah-build # task name for pipeline reference
spec: # task specification
params: # input parameters
- name: image # target image name
type: string # string type
- name: dockerfile # Dockerfile path
type: string # string type
default: ./Dockerfile # default location
- name: build-args # Docker build arguments
type: array # array type for multiple args
default: [] # empty by default
workspaces: # workspace declarations
- name: source # source code workspace
results: # task results
- name: image-digest # output image digest
description: Image digest after push # result description
steps: # execution steps
- name: build # buildah build step
image: quay.io/buildah/buildah:v1.33 # buildah container image
script: | # inline build script
#!/bin/bash # bash interpreter
buildah bud \ # build using Dockerfile
--storage-driver=vfs \ # use vfs for unprivileged builds
--format=oci \ # OCI image format
--tls-verify=true \ # verify TLS certificates
--layers \ # enable layer caching
-f $(params.dockerfile) \ # specify Dockerfile path
-t $(params.image) \ # tag the built image
$(workspaces.source.path) # build context directory
buildah push \ # push image to registry
--storage-driver=vfs \ # consistent storage driver
--tls-verify=true \ # verify registry TLS
--digestfile=$(results.image-digest.path) \ # write digest to results
$(params.image) \ # source image to push
docker://$(params.image) # destination registry URL
securityContext: # pod security settings
runAsUser: 0 # run as root for buildah
volumeMounts: # mount volumes
- name: varlibcontainers # container storage volume
mountPath: /var/lib/containers # buildah storage path
volumes: # volume definitions
- name: varlibcontainers # container storage volume
emptyDir: {} # ephemeral storage
---
# PipelineRun using the Kaniko build task
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: PipelineRun # PipelineRun resource
metadata: # resource metadata
generateName: payments-api-build- # auto-generated run name
spec: # run specification
pipelineRef: # reference to pipeline
name: payments-api-pipeline # pipeline to execute
serviceAccountName: payments-pipeline-sa # SA with registry credentials
params: # pipeline parameters
- name: image # image name parameter
value: registry.acme.io/payments-api:v2.3.0 # full image reference
workspaces: # workspace bindings
- name: source # source code workspace
volumeClaimTemplate: # dynamic PVC for source
spec: # PVC specification
accessModes: # access mode list
- ReadWriteOnce # single-node read-write
resources: # resource requirements
requests: # storage request
storage: 2Gi # 2 GiB for source and build
- name: docker-credentials # registry credentials workspace
secret: # mount from Kubernetes Secret
secretName: acme-registry-dockerconfig # docker config secret nameInterview Tip
A junior engineer typically defaults to Docker-in-Docker for container builds without understanding why Tekton pipelines avoid the Docker daemon. Interviewers want you to explain the security implications of running a Docker socket inside CI pods and why daemonless builders like Kaniko and Buildah are the standard in Kubernetes-native CI. Compare Kaniko's fully unprivileged userspace execution with Buildah's need for certain Linux capabilities. Discuss layer caching strategies: Kaniko's --cache=true with a remote cache repository versus Buildah's local layer caching with persistent volumes. Always mention writing the image digest to Tekton Results for downstream immutability verification. If you can describe how you configured multi-registry pushes for disaster recovery or how you reduced build times from eight minutes to two minutes with layer caching in a real microservice pipeline, you demonstrate production optimization skills that strongly differentiate you.
◈ Architecture Diagram
┌─────────────────────────────────────────────────────────┐
│ Container Image Build in Tekton │
└────────────────────────────┬────────────────────────────┘
│
┌──────────────────────────┴──────────────────────────┐
│ TaskRun Pod │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Workspace: source │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ /workspace/source/ │ │ │
│ │ │ ├── Dockerfile │ │ │
│ │ │ ├── src/ │ │ │
│ │ │ └── go.mod │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └──────────────────────┬───────────────────────┘ │
│ │ │
│ ┌────────────┴────────────┐ │
│ ↓ ↓ │
│ ┌──────────────────┐ ┌───────────────────────┐ │
│ │ Option A: │ │ Option B: │ │
│ │ Kaniko │ │ Buildah │ │
│ │ │ │ │ │
│ │ ● Unprivileged │ │ ● Dockerfile + CLI │ │
│ │ ● No daemon │ │ ● --storage-driver │ │
│ │ ● --cache=true │ │ =vfs │ │
│ │ ● --destination │ │ ● buildah bud + push │ │
│ └────────┬─────────┘ └───────────┬───────────┘ │
│ │ │ │
│ └────────────┬───────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Results: │ │
│ │ ● image-digest: sha256:a1b2c3d4... │ │
│ │ ● image-url: registry.acme.io/app@sha256:.. │ │
│ └──────────────────────┬───────────────────────┘ │
└─────────────────────────┼───────────────────────────┘
│
↓
┌──────────────────────────┐
│ Container Registry │
│ registry.acme.io │
│ ● payments-api:v2.3.0 │
│ ● payments-api@sha256: │
└──────────────────────────┘💬 Comments
Quick Answer
The Tekton Catalog is a community-maintained collection of reusable Tasks and Pipelines hosted on Tekton Hub that you can install into your cluster using kubectl or the tkn CLI, enabling teams to leverage pre-built, tested components for common CI/CD operations like git cloning, image building, and deployment.
Detailed Answer
Think of the Tekton Catalog like an app store for CI/CD building blocks. Instead of every team writing their own git-clone Task, image-build Task, and deployment Task from scratch, the Tekton Hub provides pre-built, community-tested components that you can install with a single command. Just as you would not write your own web browser when you can download one, you should not write your own git-clone Task when a battle-tested version already exists in the catalog.
The Tekton Catalog lives in the tektoncd/catalog GitHub repository and is searchable through hub.tekton.dev, the official Tekton Hub web interface. The catalog organizes Tasks and Pipelines by category, including git operations, image building, testing frameworks, deployment tools, notification services, and cloud provider integrations. Each catalog entry includes a README with usage instructions, parameter documentation, workspace requirements, and example PipelineRun manifests. Tasks are versioned following semantic versioning, allowing you to pin specific versions in your pipelines for reproducibility. The git-clone Task, for example, has gone through multiple major versions, each improving performance, adding features like sparse checkout and submodule support, and fixing edge cases discovered by the community.
Installing a catalog Task into your cluster can be done through several methods. The simplest approach uses kubectl apply with the raw GitHub URL pointing to the specific Task version YAML. The tkn CLI provides a more ergonomic experience with tkn hub install task git-clone which automatically resolves the latest version and applies it to your cluster. For production environments, the recommended approach is to use Tekton Bundles, which package Task definitions as OCI artifacts stored in your container registry. This ensures that your pipeline references are immutable and versioned, preventing a scenario where someone updates the catalog Task in-place and breaks running pipelines. When the checkout-service pipeline references the git-clone Task, using a bundle reference guarantees that every execution uses the exact same Task definition regardless of what happens in the upstream catalog.
Customizing catalog Tasks is a common requirement since generic Tasks rarely match every team's exact needs. Rather than forking and modifying catalog Tasks directly, the recommended pattern is to wrap them in a Pipeline that provides organization-specific defaults and add custom pre-processing or post-processing steps as separate Tasks. For instance, the generic git-clone Task works for any repository, but your organization might need to configure a corporate proxy, use an internal certificate authority, or apply specific Git LFS settings. Instead of modifying git-clone, create a Pipeline that runs a configure-git-environment Task before git-clone, setting up the necessary environment. If the catalog Task truly needs modification, vendor it into your organization's internal Task registry by copying it, making changes, and publishing it as a Tekton Bundle to your private registry. This gives you full control while maintaining a clear lineage back to the original catalog Task.
In production, teams managing microservices like payments-api, order-processing-service, and inventory-sync benefit enormously from standardized catalog Tasks. The platform engineering team maintains an internal catalog of blessed Tasks that have been security-reviewed, performance-tested, and configured with organization-specific defaults. Each service team builds their Pipeline by composing these standard Tasks rather than writing custom implementations. This standardization reduces maintenance burden, ensures consistent security practices across all pipelines, and accelerates onboarding of new services. When a vulnerability is discovered in a catalog Task, the platform team updates it once in the internal registry, and all pipelines pick up the fix on their next run. Monitor catalog Task usage across your cluster with Tekton Dashboard and set up alerts for deprecated Task versions to maintain hygiene across dozens of pipelines.
Code Example
# Install the git-clone Task from Tekton Hub using tkn CLI
tkn hub install task git-clone \
--version 0.9 # install specific version 0.9
---
# Install a Task directly from the catalog GitHub repository
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/git-clone/0.9/git-clone.yaml # apply git-clone v0.9 from catalog
---
# Install the kaniko Task from the catalog
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/kaniko/0.6/kaniko.yaml # apply kaniko v0.6 from catalog
---
# Install the kubernetes-actions Task for deployment
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/kubernetes-actions/0.2/kubernetes-actions.yaml # apply k8s-actions v0.2
---
# List installed Tasks in the cluster
tkn task list \ # list all tasks in namespace
--namespace tekton-pipelines # specify the namespace
---
# Search for Tasks on Tekton Hub
tkn hub search git # search hub for git-related tasks
---
# Create a Tekton Bundle for version pinning
tkn bundle push registry.acme.io/tekton-tasks/git-clone:0.9 \ # push task as OCI bundle
-f git-clone-v0.9.yaml # from local task YAML file
---
# Pipeline composing catalog Tasks for order-processing-service
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Pipeline # Pipeline resource definition
metadata: # resource metadata
name: order-processing-pipeline # pipeline name
spec: # pipeline specification
params: # pipeline parameters
- name: repo-url # repository URL parameter
type: string # string type
- name: revision # git revision parameter
type: string # string type
default: main # default to main branch
- name: image-name # target image name
type: string # string type
workspaces: # pipeline workspaces
- name: shared-workspace # shared workspace for all tasks
- name: docker-credentials # registry credentials workspace
tasks: # pipeline tasks using catalog
- name: fetch-source # clone task using catalog git-clone
taskRef: # reference to catalog task
name: git-clone # catalog git-clone task
workspaces: # workspace bindings
- name: output # git-clone output workspace name
workspace: shared-workspace # bind to pipeline workspace
params: # task parameters
- name: url # repository URL
value: $(params.repo-url) # from pipeline parameter
- name: revision # git revision
value: $(params.revision) # from pipeline parameter
- name: depth # clone depth
value: "1" # shallow clone for speed
- name: run-tests # unit test task
taskRef: # reference to custom task
name: golang-test # custom Go test task
runAfter: # ordering dependency
- fetch-source # run after clone completes
workspaces: # workspace bindings
- name: source # source code workspace
workspace: shared-workspace # bind to pipeline workspace
- name: build-image # build using catalog kaniko task
taskRef: # reference to catalog task
name: kaniko # catalog kaniko task
runAfter: # ordering dependency
- run-tests # run after tests pass
workspaces: # workspace bindings
- name: source # source code workspace
workspace: shared-workspace # bind to pipeline workspace
- name: dockerconfig # docker credentials workspace
workspace: docker-credentials # bind to credentials workspace
params: # task parameters
- name: IMAGE # target image parameter
value: $(params.image-name) # from pipeline parameter
- name: EXTRA_ARGS # additional kaniko arguments
value: # array of extra flags
- --cache=true # enable layer caching
- --cache-ttl=24h # cache TTL of 24 hoursInterview Tip
A junior engineer typically describes the Tekton Catalog as just a GitHub repository with YAML files, but interviewers want to hear about production-grade consumption patterns. Explain version pinning using specific catalog versions or Tekton Bundles stored as OCI artifacts in your private registry. Discuss the difference between directly applying catalog Tasks via kubectl versus bundling them for immutable references. Address customization strategies: wrapping catalog Tasks in Pipelines with organization-specific defaults rather than forking and maintaining separate copies. Mention the platform engineering pattern where an internal blessed catalog provides security-reviewed, performance-tested Tasks that all service teams compose into their pipelines. If you can explain how updating a single catalog Task in the internal registry propagates fixes across dozens of microservice pipelines, you demonstrate the operational leverage that impresses senior interviewers.
◈ Architecture Diagram
┌─────────────────────────────────────────────────────────┐
│ Tekton Catalog Ecosystem │
└────────────────────────────┬────────────────────────────┘
│
┌──────────────────────────┴──────────────────────────┐
│ ┌──────────────────────────────────────────────┐ │
│ │ hub.tekton.dev (Tekton Hub) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │git-clone │ │ kaniko │ │ k8s-actions │ │ │
│ │ │ v0.9 │ │ v0.6 │ │ v0.2 │ │ │
│ │ └─────┬────┘ └─────┬────┘ └──────┬───────┘ │ │
│ └────────┼────────────┼─────────────┼──────────┘ │
└───────────┼────────────┼─────────────┼──────────────┘
│ │ │
↓ ↓ ↓
┌────────────────────────────────────────────────────┐
│ Installation Methods │
│ │
│ ● tkn hub install task git-clone --version 0.9 │
│ ● kubectl apply -f <catalog-url> │
│ ● tkn bundle push (OCI artifact) │
└───────────────────────┬────────────────────────────┘
│
↓
┌────────────────────────────────────────────────────┐
│ Internal Task Registry (Recommended) │
│ ┌──────────────────────────────────────────────┐ │
│ │ registry.acme.io/tekton-tasks/ │ │
│ │ ● git-clone:0.9 (bundled) │ │
│ │ ● kaniko:0.6 (bundled) │ │
│ │ ● acme-deploy:1.2 (custom) │ │
│ │ ● acme-notify:1.0 (custom) │ │
│ └──────────────────┬───────────────────────────┘ │
└─────────────────────┼──────────────────────────────┘
│
┌────────────┼────────────┐
↓ ↓ ↓
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ payments-api │ │ order- │ │ inventory- │
│ pipeline │ │ proc │ │ sync │
│ │ │ pipeline │ │ pipeline │
│ taskRef: │ │ taskRef: │ │ taskRef: │
│ git-clone │ │ git- │ │ git-clone │
│ kaniko │ │ clone │ │ kaniko │
│ acme-deploy │ │ kaniko │ │ acme-deploy │
└──────────────┘ └──────────┘ └──────────────┘💬 Comments
Quick Answer
A Tekton CI/CD pipeline for microservices chains Tasks for source cloning, unit testing, container image building, security scanning, and Kubernetes deployment into a Pipeline resource, using workspaces for shared data, Results for artifact tracing, and Triggers for automated execution on git push events.
Detailed Answer
Think of a Tekton CI/CD pipeline for microservices like an automotive assembly line. Each station on the line performs a specialized task: welding, painting, quality inspection, and final assembly. Parts move between stations on a conveyor belt. In Tekton, each station is a Task, the conveyor belt is a workspace, quality gates are when expressions, and the factory floor manager who decides when to start the line is a Trigger. The result is a fully automated, repeatable process that transforms source code into a running, tested, production-ready service.
The pipeline architecture for a microservice like payments-api typically consists of six to eight sequential and parallel Tasks. The first Task, git-clone from the Tekton Catalog, checks out the source code into a shared workspace. The next stage fans out into parallel Tasks: a lint-and-format Task checks code style, a unit-test Task runs the test suite, and a dependency-audit Task scans for vulnerable dependencies. These three can run simultaneously because they all read from the same workspace without conflicting writes. After all three parallel Tasks succeed, the pipeline converges on a build-and-push Task that uses Kaniko to create a container image and push it to the registry, emitting the image digest as a Tekton Result. A security-scan Task then runs Trivy or Grype against the built image using the digest result, and emits a scan-status result. Finally, a deploy Task uses kubectl or Helm to update the Kubernetes Deployment with the new image digest, but only if the security scan passed, enforced by a when expression.
Workspace design is critical for microservice pipelines. A single PersistentVolumeClaim workspace shared across all Tasks provides the source code, build artifacts, and test reports. Using a volumeClaimTemplate in the PipelineRun automatically provisions a fresh PVC for each run, ensuring isolation between concurrent pipeline executions for different services. The access mode must be ReadWriteOnce for most cloud providers, which means all Tasks sharing the workspace must run on the same node. If your cluster uses multiple availability zones, configure a StorageClass with volumeBindingMode: WaitForFirstConsumer to ensure the PVC is created in the zone where the first Task pod is scheduled. For the order-processing-service, which has a large test data directory, allocating a 5Gi PVC prevents out-of-space errors during parallel test execution.
In a microservices architecture with services like payments-api, user-auth-service, order-processing-service, inventory-sync, and checkout-service, you need a strategy for managing multiple pipelines efficiently. The recommended approach is creating a parameterized Pipeline template that accepts the repository URL, image name, deployment name, and namespace as parameters. Each service uses the same Pipeline definition with different PipelineRun parameters, avoiding pipeline duplication. Tekton Triggers handles the automation: a single EventListener receives webhooks from all repositories, CEL interceptors route events to the correct TriggerTemplate based on the repository name, and each TriggerTemplate creates a PipelineRun with service-specific parameters. This architecture means adding a new microservice to the CI/CD system requires only a new TriggerTemplate and TriggerBinding, not a new Pipeline.
Production hardening involves several critical considerations. Set resource requests and limits on every Task step to prevent build pods from consuming excessive cluster resources and starving other workloads. Configure timeouts at both the Task and Pipeline levels to prevent hung builds from occupying cluster resources indefinitely. Use the finally block in the Pipeline to define cleanup Tasks that run regardless of pipeline success or failure, such as sending Slack notifications, updating deployment status in a dashboard, or cleaning up temporary cloud resources. Implement Tekton Chains for supply chain security, which automatically signs TaskRun and PipelineRun attestations and generates SLSA provenance documents. For the checkout-service handling financial transactions, this provenance chain provides auditable proof that every deployed image was built from a specific commit, passed all tests, cleared the security scan, and was deployed by an authorized pipeline execution.
Code Example
# Complete CI/CD Pipeline for payments-api microservice
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: Pipeline # Pipeline resource definition
metadata: # resource metadata
name: microservice-cicd-pipeline # reusable pipeline name
spec: # pipeline specification
params: # parameterized for any microservice
- name: repo-url # git repository URL
type: string # string type
- name: revision # git commit or branch
type: string # string type
default: main # default to main branch
- name: image-name # container image name
type: string # string type
- name: deploy-name # kubernetes deployment name
type: string # string type
- name: deploy-namespace # target namespace
type: string # string type
default: production # default to production
workspaces: # pipeline workspace declarations
- name: source # shared source code workspace
- name: docker-creds # registry credentials
tasks: # pipeline task definitions
- name: clone # step 1: clone source code
taskRef: # reference to catalog task
name: git-clone # Tekton Hub git-clone task
workspaces: # workspace bindings
- name: output # git-clone output workspace
workspace: source # bind to shared workspace
params: # clone parameters
- name: url # repository URL
value: $(params.repo-url) # from pipeline parameter
- name: revision # git revision
value: $(params.revision) # from pipeline parameter
- name: unit-test # step 2a: run unit tests (parallel)
taskRef: # reference to test task
name: run-unit-tests # custom unit test task
runAfter: # ordering dependency
- clone # after clone completes
workspaces: # workspace bindings
- name: source # source workspace
workspace: source # bind to shared workspace
- name: lint # step 2b: code linting (parallel)
taskRef: # reference to lint task
name: run-linter # custom linting task
runAfter: # ordering dependency
- clone # after clone completes
workspaces: # workspace bindings
- name: source # source workspace
workspace: source # bind to shared workspace
- name: build-image # step 3: build container image
taskRef: # reference to build task
name: kaniko-build # Kaniko build task
runAfter: # ordering dependencies
- unit-test # after tests pass
- lint # after linting passes
workspaces: # workspace bindings
- name: source # source code workspace
workspace: source # bind to shared workspace
- name: docker-credentials # registry credentials
workspace: docker-creds # bind to credentials workspace
params: # build parameters
- name: image # target image name
value: $(params.image-name) # from pipeline parameter
- name: security-scan # step 4: scan built image
taskRef: # reference to scan task
name: trivy-image-scan # Trivy scanner task
runAfter: # ordering dependency
- build-image # after image is built
params: # scan parameters
- name: image-digest # image to scan
value: $(tasks.build-image.results.image-digest) # from build result
- name: image-url # full image URL
value: $(tasks.build-image.results.image-url) # from build result
- name: deploy # step 5: deploy to kubernetes
when: # conditional deployment
- input: $(tasks.security-scan.results.scan-status) # check scan result
operator: In # must be in allowed values
values: ["pass"] # only deploy if scan passed
taskRef: # reference to deploy task
name: kubectl-deploy # kubernetes deployment task
runAfter: # ordering dependency
- security-scan # after scan completes
params: # deployment parameters
- name: image-url # image to deploy
value: $(tasks.build-image.results.image-url) # from build result
- name: deployment-name # k8s deployment name
value: $(params.deploy-name) # from pipeline parameter
- name: namespace # target namespace
value: $(params.deploy-namespace) # from pipeline parameter
finally: # tasks that always run
- name: notify # notification task
taskRef: # reference to notify task
name: send-slack-notification # Slack notification task
params: # notification parameters
- name: pipeline-name # pipeline name for message
value: $(context.pipeline.name) # from pipeline context
- name: status # pipeline status
value: $(tasks.status) # aggregated task status
---
# PipelineRun for payments-api service
apiVersion: tekton.dev/v1beta1 # Tekton Pipelines API version
kind: PipelineRun # PipelineRun resource
metadata: # resource metadata
generateName: payments-api- # auto-generated name prefix
spec: # run specification
pipelineRef: # reference to pipeline
name: microservice-cicd-pipeline # the reusable pipeline
serviceAccountName: cicd-pipeline-sa # service account with permissions
timeout: 30m # pipeline-level timeout
params: # service-specific parameters
- name: repo-url # payments-api repo URL
value: https://github.com/acme/payments-api # specific repo
- name: revision # git revision
value: abc123def # specific commit SHA
- name: image-name # payments-api image
value: registry.acme.io/payments-api:v3.1.0 # versioned image
- name: deploy-name # k8s deployment name
value: payments-api # deployment to update
- name: deploy-namespace # target namespace
value: payments # payments namespace
workspaces: # workspace bindings
- name: source # source code workspace
volumeClaimTemplate: # dynamic PVC per run
spec: # PVC specification
accessModes: # access modes
- ReadWriteOnce # single-node access
resources: # resource requirements
requests: # storage request
storage: 5Gi # 5 GiB for large repos
- name: docker-creds # registry credentials
secret: # from kubernetes secret
secretName: registry-dockerconfig # docker config secretInterview Tip
A junior engineer typically describes a linear pipeline without considering parallelism, error handling, or multi-service reusability. Interviewers want you to demonstrate a production-grade pipeline architecture. Explain how you fan out parallel Tasks after git-clone for independent operations like linting, unit testing, and dependency auditing, then converge on the build step. Discuss workspace design with volumeClaimTemplate for per-run isolation and the ReadWriteOnce access mode constraint. Show how a parameterized Pipeline template serves multiple microservices by accepting repo-url, image-name, and deploy-namespace as parameters, with Triggers routing events to the correct PipelineRun. Mention the finally block for guaranteed notification delivery and Tekton Chains for SLSA provenance. If you can walk through how adding a new microservice to your CI/CD platform requires only a new TriggerTemplate rather than a new Pipeline, you demonstrate platform engineering maturity that senior interviewers value highly.
◈ Architecture Diagram
┌─────────────────────────────────────────────────────────┐
│ Microservice CI/CD Pipeline Architecture │
└────────────────────────────┬────────────────────────────┘
│
┌──────────────────────────┴──────────────────────────┐
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ clone (git-clone) │ │
│ │ ● fetch source → workspace │ │
│ └──────────────────┬───────────────────────────┘ │
│ │ │
│ ┌───────────┼───────────┐ │
│ ↓ ↓ ↓ │
│ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ │ unit-test │ │ lint │ │ dep- │ │
│ │ │ │ │ │ audit │ parallel │
│ │ ✓ pass │ │ ✓ pass │ │ ✓ pass │ │
│ └─────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └────────────┼────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ build-image (kaniko) │ │
│ │ ● Dockerfile → registry.acme.io/app:v3.1 │ │
│ │ results: image-digest, image-url │ │
│ └──────────────────┬───────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ security-scan (trivy) │ │
│ │ ● scan image-digest for CVEs │ │
│ │ results: scan-status = pass│fail │ │
│ └──────────────────┬───────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ deploy (when: scan-status In [pass]) │ │
│ │ ● kubectl set image → image-url@digest │ │
│ │ ● rollout status → verify healthy │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────┐ │
│ │ finally: notify │ │
│ │ ● Slack notification with pipeline status │ │
│ │ ● runs regardless of success or failure │ │
│ └──────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────┘💬 Comments
Quick Answer
Debugging failing Tekton runs involves inspecting TaskRun and PipelineRun status conditions with kubectl describe, examining step container logs with tkn taskrun logs, checking pod events for scheduling or resource issues, and using the Tekton Dashboard for visual pipeline execution tracing.
Detailed Answer
Think of debugging Tekton pipelines like troubleshooting a car that will not start. You do not immediately tear apart the engine. Instead, you follow a systematic diagnostic checklist: check fuel, check battery, check starter motor, check spark plugs. Each check narrows down the problem. In Tekton, you follow a similar layered approach: start with the PipelineRun status, drill into the specific failed TaskRun, examine the step container logs, and inspect the underlying pod events. This top-down methodology prevents wasting time on irrelevant components.
The first diagnostic step is examining the PipelineRun or TaskRun status using kubectl describe or tkn pipelinerun describe. The status section contains a conditions array with the overall run status, a childReferences or taskRuns map showing the status of each individual Task, and timestamps for when each Task started and completed. A PipelineRun can fail for several reasons: a Task step exited with a non-zero exit code, a when expression evaluated to false causing unexpected skips, a timeout was exceeded, or a workspace binding failed. The tkn pipelinerun describe command provides a human-readable summary showing which Tasks succeeded with a checkmark, which failed with an X, which were skipped, and which timed out. For the payments-api pipeline, if the security-scan Task failed, this summary immediately tells you where to focus your investigation.
Drilling into the failed TaskRun reveals step-level details. Each Task can contain multiple steps, and Tekton tracks the exit code and termination reason for every step container. The tkn taskrun logs command streams the logs from all step containers in execution order, or you can target a specific step with the --step flag. For long-running Tasks in the order-processing-service pipeline, filtering logs to the failing step avoids scrolling through megabytes of irrelevant output. If the step container was OOMKilled, the termination reason in the TaskRun status will indicate this, pointing to a resource limits issue rather than a code bug. If the step exited with a specific error code, the container logs usually contain the stack trace or error message that explains the failure.
Pod-level issues represent a separate class of failures that manifest before any step container even starts. Common pod-level problems include ImagePullBackOff when the step container image cannot be pulled from the registry, pending pods due to insufficient cluster resources or node affinity rules that cannot be satisfied, PVC binding failures when the workspace volume cannot be provisioned, and service account permission errors when the TaskRun SA lacks RBAC permissions to create or access resources. Use kubectl describe pod on the TaskRun pod to inspect the events section, which contains detailed messages about scheduling decisions, image pulls, volume mounts, and container starts. For the user-auth-service pipeline, an ImagePullBackOff on the custom test runner image indicates either incorrect image tags, missing registry credentials, or a registry outage, and the pod events will specify which.
Advanced debugging techniques include using the Tekton Dashboard for visual pipeline execution tracing, which shows the DAG of Tasks with color-coded statuses and clickable log viewers. For intermittent failures, examine the TaskRun's retries field if retry policies are configured, and compare timestamps between the failing run and surrounding successful runs to identify environmental changes. Enable verbose logging in the Tekton controller by modifying the config-logging ConfigMap in the tekton-pipelines namespace, which provides detailed information about Pipeline validation, Task scheduling, and result propagation. For workspace-related failures, exec into a debug pod using the same PVC to inspect file permissions, disk space, and directory structure. Always check whether the failure is deterministic by re-running the PipelineRun with the same parameters, as transient failures from network timeouts, registry rate limits, or resource pressure require different remediation than code-level bugs.
Code Example
# List all PipelineRuns to find the failing one
tkn pipelinerun list \ # list pipeline runs
--namespace tekton-pipelines # in the tekton namespace
---
# Describe the failed PipelineRun for status overview
tkn pipelinerun describe payments-api-run-x7k2m \ # describe specific run
--namespace tekton-pipelines # in the tekton namespace
---
# Get detailed PipelineRun status via kubectl
kubectl describe pipelinerun payments-api-run-x7k2m \ # kubectl describe for full details
--namespace tekton-pipelines # in the tekton namespace
---
# View logs from the failed TaskRun
tkn taskrun logs payments-api-run-x7k2m-build-image \ # stream logs from specific taskrun
--namespace tekton-pipelines # in the tekton namespace
---
# View logs for a specific step within the TaskRun
tkn taskrun logs payments-api-run-x7k2m-build-image \ # target the build taskrun
--step build-and-push \ # only show logs from this step
--namespace tekton-pipelines # in the tekton namespace
---
# List pods created by the PipelineRun
kubectl get pods \ # list pods
--selector=tekton.dev/pipelineRun=payments-api-run-x7k2m \ # filter by pipelinerun label
--namespace tekton-pipelines # in the tekton namespace
---
# Describe the pod for events and scheduling details
kubectl describe pod payments-api-run-x7k2m-build-image-pod \ # describe the taskrun pod
--namespace tekton-pipelines # in the tekton namespace
---
# Check container exit codes and termination reasons
kubectl get pod payments-api-run-x7k2m-build-image-pod \ # get pod details
--namespace tekton-pipelines \ # in the tekton namespace
-o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.state}{"\n"}{end}' # extract container states
---
# View Tekton controller logs for pipeline validation errors
kubectl logs deployment/tekton-pipelines-controller \ # stream controller logs
--namespace tekton-pipelines \ # controller namespace
--tail=100 # last 100 lines
---
# Check workspace PVC status for volume binding issues
kubectl get pvc \ # list persistent volume claims
--selector=tekton.dev/pipelineRun=payments-api-run-x7k2m \ # filter by pipelinerun
--namespace tekton-pipelines # in the tekton namespace
---
# Re-run a failed PipelineRun with same parameters
tkn pipeline start payments-api-pipeline \ # start the pipeline again
--use-pipelinerun payments-api-run-x7k2m \ # reuse params from failed run
--namespace tekton-pipelines # in the tekton namespace
---
# Enable verbose Tekton controller logging
kubectl patch configmap config-logging \ # patch the logging configmap
--namespace tekton-pipelines \ # in the tekton-pipelines namespace
--type merge \ # merge patch type
-p '{"data":{"loglevel.controller":"debug"}}' # set controller log level to debug
---
# Check TaskRun results for debugging data flow
kubectl get taskrun payments-api-run-x7k2m-build-image \ # get taskrun details
--namespace tekton-pipelines \ # in the tekton namespace
-o jsonpath='{.status.taskResults[*]}' # extract task results from statusInterview Tip
A junior engineer typically jumps straight to reading step logs without systematically diagnosing the failure layer. Interviewers want to see a structured top-down debugging approach. Start by describing the PipelineRun to identify which Task failed and whether it was a step failure, timeout, skip, or pod-level issue. Then drill into the TaskRun logs for the specific failing step. Explain how to differentiate between code-level failures with non-zero exit codes, resource-level failures like OOMKilled or disk pressure, infrastructure-level failures like ImagePullBackOff or PVC binding errors, and configuration-level failures like missing parameters or incorrect workspace bindings. Mention the Tekton Dashboard for visual tracing and controller logs for validation errors. If you can describe a real debugging scenario where you traced a payments-api pipeline failure from PipelineRun status through pod events to discover an expired registry credential, you demonstrate the operational experience that interviewers value highly.
◈ Architecture Diagram
┌─────────────────────────────────────────────────────────┐
│ Tekton Debugging Decision Tree │
└────────────────────────────┬────────────────────────────┘
│
↓
┌──────────────────────────┐
│ PipelineRun Failed │
│ tkn pipelinerun describe│
└────────────┬─────────────┘
│
┌────────────┴────────────┐
│ Which Task failed? │
│ ✓ clone │
│ ✓ unit-test │
│ ✗ build-image ← HERE │
│ ○ security-scan │
│ ○ deploy │
└────────────┬────────────┘
│
┌────────────┴────────────┐
│ TaskRun Status Check │
└──┬─────────┬────────┬───┘
│ │ │
↓ ↓ ↓
┌──────────────┐ ┌───────────┐ ┌──────────────┐
│ Step Failure │ │ Pod Issue │ │ Timeout │
│ │ │ │ │ │
│ exit code≠0 │ │ ImagePull │ │ exceeded │
│ OOMKilled │ │ BackOff │ │ deadline │
│ │ │ Pending │ │ │
│ Action: │ │ PVC error │ │ Action: │
│ tkn taskrun │ │ │ │ increase │
│ logs --step │ │ Action: │ │ timeout or │
│ │ │ kubectl │ │ optimize │
│ │ │ describe │ │ task steps │
│ │ │ pod │ │ │
└──────┬───────┘ └─────┬─────┘ └──────┬───────┘
│ │ │
└───────────────┼──────────────┘
↓
┌──────────────────────────┐
│ Resolution │
│ ● Fix code/config │
│ ● Adjust resources │
│ ● Fix credentials │
│ ● Re-run pipeline │
│ tkn pipeline start │
│ --use-pipelinerun │
└──────────────────────────┘💬 Comments
Quick Answer
Tekton Chains is a Kubernetes controller that automatically signs TaskRun and PipelineRun results using cryptographic keys, generating in-toto attestations and SLSA provenance metadata. It integrates with Sigstore's cosign for keyless signing and stores signatures in OCI registries or transparency logs, providing end-to-end supply chain integrity verification for every artifact produced by Tekton pipelines.
Detailed Answer
Tekton Chains addresses one of the most critical challenges in modern software delivery: proving that a container image or artifact was actually built by your CI/CD system and has not been tampered with between build and deployment. In a world where supply chain attacks like SolarWinds and Codecov have compromised thousands of organizations, simply building an image is no longer sufficient. You must cryptographically prove who built it, what source code was used, what build steps were executed, and that the resulting artifact is identical to what was produced. Tekton Chains automates this entire provenance chain without requiring developers to modify their pipelines.
Tekton Chains operates as a Kubernetes controller that watches for completed TaskRuns and PipelineRuns. When the payments-api pipeline finishes building a container image, Chains automatically captures the build metadata including the git commit SHA, the Dockerfile used, the base image digest, and all input parameters. It then generates an in-toto attestation document conforming to the SLSA (Supply-chain Levels for Software Artifacts) framework. This attestation is cryptographically signed using either a locally stored key pair, a KMS-managed key from AWS KMS, GCP KMS, or HashiCorp Vault, or Sigstore's Fulcio for keyless signing where the identity is bound to an OIDC token from the Kubernetes service account. The signed attestation is then stored alongside the artifact in the OCI registry, attached as a cosign signature or uploaded to a Rekor transparency log for public verifiability.
In a production environment at enterprise scale, implementing Chains for the order-processing-service and inventory-sync pipelines requires careful architecture decisions. The signing key management strategy is paramount. Organizations handling financial transactions through their checkout-service typically use hardware-backed KMS keys with strict IAM policies, ensuring that only the Tekton Chains controller service account can access the signing key. The transparency log integration means every signed artifact is recorded in an immutable append-only ledger, making it impossible to retroactively modify or deny that a particular build occurred. For the user-auth-service, this creates a complete audit trail from git commit to running container that satisfies SOC 2, FedRAMP, and PCI-DSS compliance requirements without manual evidence collection.
The verification side of the equation is equally important. When ArgoCD or a Kubernetes admission controller like Kyverno or OPA Gatekeeper attempts to deploy the payments-api image, it first verifies the cosign signature and validates the SLSA attestation against organizational policies. These policies can enforce requirements such as the image was built from the main branch of an approved repository, the build pipeline included a security scanning step that passed, the source code had at least two approved reviewers, and the build occurred on a trusted build cluster. If any of these conditions fail verification, the deployment is blocked before the image ever reaches the production cluster. This creates a cryptographic trust chain from developer commit through CI/CD build to production deployment.
The operational complexity of Tekton Chains centers around key rotation, attestation format evolution, and multi-cluster deployment. Key rotation requires updating the signing key across all clusters while maintaining the ability to verify artifacts signed with the previous key during the rotation window. Organizations running the inventory-sync and checkout-service across multiple regions deploy Chains in each cluster with region-specific KMS keys, using a federated trust model where each regional key is registered as a trusted signer in the verification policies of all clusters. The SLSA framework defines four levels of assurance, and reaching SLSA Level 3 requires that the build platform itself is hardened, the build definition comes from a version-controlled source, and the provenance is non-falsifiable. Tekton Chains provides the provenance generation and signing infrastructure, but achieving SLSA Level 3 also requires securing the Tekton controller, isolating build namespaces, and ensuring that pipeline definitions cannot be modified at runtime.
Code Example
# Install Tekton Chains controller in the tekton-chains namespace
kubectl apply --filename https://storage.googleapis.com/tekton-releases/chains/latest/release.yaml
# Configure Chains to use cosign for signing artifacts
kubectl patch configmap chains-config -n tekton-chains -p '{"data":{"artifacts.taskrun.format":"in-toto"}}'
# Set the storage backend to OCI registry for attestation persistence
kubectl patch configmap chains-config -n tekton-chains -p '{"data":{"artifacts.taskrun.storage":"oci"}}'
# Enable SLSA v1 provenance format for compliance reporting
kubectl patch configmap chains-config -n tekton-chains -p '{"data":{"artifacts.taskrun.signer":"x509"}}'
# Generate a cosign key pair for artifact signing
cosign generate-key-pair k8s://tekton-chains/signing-secrets
# Create a TaskRun that builds the payments-api container image
kubectl create -f payments-api-build-taskrun.yaml
# Wait for the TaskRun to complete and Chains to sign the artifact
tkn taskrun describe payments-api-build-run-xyz --output json | jq '.metadata.annotations["chains.tekton.dev/signed"]'
# Verify the signed image attestation using cosign
cosign verify --key cosign.pub registry.company.com/payments-api:v2.3.1
# Verify the in-toto attestation attached to the image
cosign verify-attestation --key cosign.pub --type slsaprovenance registry.company.com/payments-api:v2.3.1
# Create a Kyverno policy to enforce signature verification on deployment
# apiVersion: kyverno.io/v1
# kind: ClusterPolicy
# metadata:
# name: verify-tekton-chains-signature
# spec:
# validationFailureAction: enforce
# rules:
# - name: check-image-signature
# match:
# resources:
# kinds: [Pod]
# verifyImages:
# - imageReferences: ["registry.company.com/*"]
# attestors:
# - entries:
# - keys:
# publicKeys: |-
# -----BEGIN PUBLIC KEY-----
# MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
# -----END PUBLIC KEY-----
kubectl apply -f kyverno-verify-chains-policy.yaml
# Check the Rekor transparency log for the signed artifact entry
rekor-cli search --sha $(crane digest registry.company.com/payments-api:v2.3.1)Interview Tip
A junior engineer typically mentions container image signing as a checkbox item without understanding the full provenance chain. When answering this question, walk through the complete trust model: Tekton Chains watches TaskRuns, captures build metadata into in-toto attestations conforming to the SLSA framework, signs those attestations with cosign using KMS-managed or Fulcio keyless signing, and stores the signed attestations in OCI registries or Rekor transparency logs. Then pivot to the verification side, explaining how Kyverno or OPA Gatekeeper admission controllers validate signatures and attestation predicates before allowing deployment. Discuss key rotation strategies and multi-cluster signing architectures. If you can reference a real scenario where you blocked an unauthorized image from deploying because the attestation verification failed, that demonstrates production-grade supply chain security experience that is exceptionally rare and highly valued.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────────┐ │ Tekton Chains Supply Chain Security │ │ │ │ ┌──────────────┐ ┌───────────────────┐ │ │ │ Git Commit │───→│ Tekton Pipeline │ │ │ │ (source) │ │ Build payments-api│ │ │ └──────────────┘ └────────┬──────────┘ │ │ │ │ │ ↓ │ │ ┌────────────────────┐ │ │ │ Tekton Chains │ │ │ │ Controller │ │ │ │ ● Capture metadata│ │ │ │ ● Generate SLSA │ │ │ │ ● Sign with cosign│ │ │ └────────┬───────────┘ │ │ │ │ │ ┌────────┴───────────┐ │ │ ↓ ↓ │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ OCI Registry │ │ Rekor │ │ │ │ ● Image │ │ Transparency Log │ │ │ │ ● Signature │ │ ● Immutable │ │ │ │ ● Attestation│ │ ● Public verify │ │ │ └──────┬───────┘ └──────────────────┘ │ │ │ │ │ ↓ │ │ ┌──────────────────┐ │ │ │ Admission Control│ │ │ │ Kyverno/OPA │ │ │ │ ✓ Verify sig │ │ │ │ ✓ Check policy │──→ Deploy to Production │ │ │ ✗ Block if failed│ │ │ └──────────────────┘ │ └──────────────────────────────────────────────────────────┘
💬 Comments
Quick Answer
Scaling Tekton for enterprise multi-tenancy involves namespace-based isolation with dedicated Tekton pipeline namespaces per team, Kubernetes ResourceQuotas and LimitRanges to cap compute consumption, custom PipelineRun controllers with priority queuing, and horizontal pod autoscaling for Tekton controllers. Network policies, RBAC, and pod security standards enforce tenant boundaries while shared infrastructure components like artifact caches and registry mirrors optimize resource utilization across tenants.
Detailed Answer
Enterprise Tekton scaling is fundamentally a Kubernetes multi-tenancy problem compounded by the unique characteristics of CI/CD workloads: bursty resource consumption, long-running processes that compete for cluster capacity, and the need for strong isolation between teams whose pipelines may process sensitive code and credentials. When the payments-api team triggers 50 parallel pipeline runs during a release sprint while the user-auth-service team is running integration tests, the platform must ensure fair resource distribution, prevent noisy-neighbor problems, and maintain security boundaries between tenant workloads without requiring manual intervention from platform engineers.
The namespace isolation model forms the foundation of enterprise Tekton multi-tenancy. Each team or business unit receives a dedicated namespace such as ci-payments-team, ci-checkout-team, and ci-platform-team where their PipelineRuns execute. Kubernetes ResourceQuotas are applied to each namespace to cap the total CPU, memory, and ephemeral storage that a team's pipeline runs can consume simultaneously. LimitRanges set default and maximum resource requests for individual TaskRun pods, preventing a single poorly configured task from consuming an entire node. The Tekton controller itself runs in a separate administrative namespace with elevated permissions, while tenant namespaces contain only the PipelineRuns, TaskRuns, and their associated pods. This separation means that even if a malicious or misconfigured pipeline in the inventory-sync namespace attempts to escalate privileges, namespace-scoped RBAC policies and pod security standards prevent it from accessing resources in other tenant namespaces or the Tekton controller namespace.
Resource management at enterprise scale requires moving beyond simple quotas to intelligent scheduling and priority-based queuing. Tekton's concurrency controls allow setting maximum concurrent TaskRuns per namespace, but production environments need more sophisticated approaches. A custom admission webhook or controller can implement priority queuing where production hotfix pipelines for the checkout-service bypass the queue while routine feature branch builds for the order-processing-service wait during peak periods. Node affinity and tainting strategies dedicate specific node pools to CI/CD workloads, preventing pipeline pods from competing with production application pods for compute resources. Spot instances or preemptible VMs provide cost-effective burst capacity for the less critical build workloads, with pipeline steps designed to be idempotent so they can safely retry if a spot node is reclaimed.
The Tekton controller itself becomes a scaling bottleneck in large enterprises running thousands of PipelineRuns daily across hundreds of namespaces. The controller is a single reconciliation loop that watches all PipelineRun and TaskRun resources across the cluster. At high volume, the reconciliation queue grows, increasing latency between PipelineRun creation and pod scheduling. Mitigation strategies include deploying multiple Tekton controller replicas with leader election for high availability, increasing controller resource limits and tuning garbage collection intervals for completed runs, partitioning workloads across multiple clusters with a federation layer that routes pipeline requests to the appropriate cluster based on team or workload type, and implementing aggressive pruning of completed TaskRun and PipelineRun resources to reduce the API server watch load on the controller.
Shared infrastructure optimization across tenants significantly reduces the total cost of running Tekton at scale. A cluster-wide container registry mirror caches frequently pulled base images, eliminating redundant pulls across the payments-api, user-auth-service, and inventory-sync pipeline runs. Persistent volume-backed workspace caching stores dependency downloads such as Maven repositories, npm caches, and Go module caches in shared or per-team persistent volumes, reducing build times by 40-60 percent for subsequent runs. Tekton Results, a separate component that offloads completed run data to a database, allows aggressive pruning of in-cluster resources while maintaining the full audit trail needed for compliance. The combination of these optimizations allows enterprise platforms to support thousands of daily pipeline runs with predictable performance, fair resource allocation, and strong security isolation between teams.
Code Example
# Create a dedicated namespace for the payments team CI workloads
kubectl create namespace ci-payments-team
# Apply ResourceQuota to limit the payments team total resource consumption
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: pipeline-quota
namespace: ci-payments-team
spec:
hard:
requests.cpu: "16"
requests.memory: 32Gi
limits.cpu: "32"
limits.memory: 64Gi
pods: "20"
persistentvolumeclaims: "10"
EOF
# Apply LimitRange to set default resource constraints on individual TaskRun pods
kubectl apply -f - <<EOF
apiVersion: v1
kind: LimitRange
metadata:
name: taskrun-limits
namespace: ci-payments-team
spec:
limits:
- default:
cpu: "1"
memory: 2Gi
defaultRequest:
cpu: 500m
memory: 1Gi
max:
cpu: "4"
memory: 8Gi
type: Container
EOF
# Apply NetworkPolicy to isolate tenant namespace from other CI namespaces
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-cross-tenant
namespace: ci-payments-team
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ci-payments-team
- to:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- port: 443
protocol: TCP
EOF
# Configure Tekton controller replica count for high availability
kubectl -n tekton-pipelines patch deployment tekton-pipelines-controller --type=merge -p '{"spec":{"replicas":3}}'
# Set pruner CronJob to clean up completed runs older than 24 hours
kubectl apply -f - <<EOF
apiVersion: batch/v1
kind: CronJob
metadata:
name: tekton-pruner
namespace: tekton-pipelines
spec:
schedule: "0 */6 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: pruner
image: gcr.io/tekton-releases/dogfooding/tkn:latest
command: ["tkn"]
args: ["pipelinerun", "delete", "--keep", "50", "-n", "ci-payments-team", "-f"]
restartPolicy: OnFailure
EOF
# Verify resource quota usage in the tenant namespace
kubectl describe resourcequota pipeline-quota -n ci-payments-teamInterview Tip
A junior engineer typically describes scaling Tekton as simply adding more nodes to the cluster. At the advanced level, interviewers expect a layered answer covering namespace-based tenant isolation with RBAC and network policies, ResourceQuotas and LimitRanges for fair resource distribution, priority-based queuing for critical versus routine pipelines, Tekton controller scaling through replicas and leader election, and shared infrastructure optimization such as registry mirrors and dependency caches. Discuss how you would handle the noisy-neighbor problem when one team's release sprint consumes disproportionate cluster resources. Explain the Tekton controller bottleneck at high volume and mitigation strategies including multi-cluster federation and aggressive pruning with Tekton Results for audit retention. If you can describe a production scenario where you tuned quotas after a capacity incident, such as the payments-api team's integration test suite consuming the entire node pool and blocking other teams, that operational experience demonstrates platform engineering maturity.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────────┐ │ Enterprise Multi-Tenant Tekton Architecture │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Tekton Controller Namespace │ │ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ │ │ Controller │ │ Controller │ │ Controller │ │ │ │ │ │ Replica 1 │ │ Replica 2 │ │ Replica 3 │ │ │ │ │ │ (leader) │ │ (standby) │ │ (standby) │ │ │ │ │ └────────────┘ └────────────┘ └────────────┘ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ │ ┌────────────────┼────────────────┐ │ │ ↓ ↓ ↓ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ci-payments │ │ci-checkout │ │ci-platform │ │ │ │ │ │ │ │ │ │ │ │ResourceQuota:│ │ResourceQuota:│ │ResourceQuota:│ │ │ │ CPU: 16 core │ │ CPU: 8 core │ │ CPU: 12 core │ │ │ │ Mem: 32Gi │ │ Mem: 16Gi │ │ Mem: 24Gi │ │ │ │ Pods: 20 │ │ Pods: 10 │ │ Pods: 15 │ │ │ │ │ │ │ │ │ │ │ │NetworkPolicy │ │NetworkPolicy │ │NetworkPolicy │ │ │ │ ✗ cross-ns │ │ ✗ cross-ns │ │ ✗ cross-ns │ │ │ │ ✓ egress 443 │ │ ✓ egress 443 │ │ ✓ egress 443 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Shared Infrastructure │ │ │ │ ● Registry Mirror ● Dependency Cache ● Results DB│ │ │ └─────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘
💬 Comments
Quick Answer
Tekton handles the CI phase by building, testing, and publishing artifacts, then updates a GitOps repository with the new image tag or manifest changes. ArgoCD watches the GitOps repository and automatically reconciles the Kubernetes cluster state to match the declared configuration. The integration point is the Git commit that Tekton pushes to the deployment repository, creating a clean separation between continuous integration and continuous delivery.
Detailed Answer
The Tekton-ArgoCD integration represents the canonical implementation of the GitOps model where CI and CD are deliberately separated with Git as the single source of truth between them. This separation is not merely architectural elegance but a fundamental security and auditability requirement. The CI system (Tekton) has permissions to access source code repositories, build infrastructure, and artifact registries, while the CD system (ArgoCD) has permissions to deploy to Kubernetes clusters. Neither system needs the other's credentials. The Git repository that sits between them serves as an auditable, version-controlled contract that captures every deployment decision as a git commit with full attribution and rollback capability.
The integration workflow begins when a developer pushes code to the payments-api source repository. A Tekton EventListener receives the webhook from GitHub or GitLab and triggers a PipelineRun. The Tekton pipeline clones the source code, runs unit tests and security scans, builds the container image, pushes it to the registry with a digest-based tag, and then executes the critical handoff step: updating the GitOps deployment repository. This update step clones the deployment repository containing the Kubernetes manifests or Helm values for the payments-api, modifies the image tag to reference the newly built image digest, commits the change with a meaningful message linking back to the source commit and pipeline run, and pushes to the deployment repository. This git commit is the integration point between Tekton and ArgoCD.
ArgoCD is configured with an Application resource that points to the payments-api directory in the GitOps deployment repository. ArgoCD continuously polls the repository or receives webhook notifications when changes are pushed. When ArgoCD detects that the desired state in Git differs from the actual state in the Kubernetes cluster, it performs a sync operation that applies the updated manifests. For the order-processing-service and checkout-service, separate ArgoCD Application resources point to their respective directories in the same or different deployment repositories. ArgoCD's sync policies can be configured for automatic sync with auto-prune and self-heal enabled for development environments, while production environments use manual sync with required approvals through ArgoCD's sync windows and RBAC policies.
The deployment repository structure is critical for this integration to work cleanly at scale. A well-designed GitOps repository uses a directory hierarchy that separates environments and services. The base directory contains the common Kubernetes manifests for each service, while overlay directories for dev, staging, and production contain environment-specific patches using Kustomize. When Tekton updates the image tag, it modifies only the environment-specific overlay, allowing the same pipeline to promote images through environments by targeting different overlay directories. For Helm-based deployments, the deployment repository contains values files per environment, and Tekton updates the image.tag value in the appropriate values file. This structure allows ArgoCD to manage dozens of services across multiple environments from a single repository with clear separation of concerns.
Operational considerations for the Tekton-ArgoCD integration include handling deployment failures, managing secrets, and implementing progressive delivery. When ArgoCD detects a failed deployment through health checks on the newly deployed pods of the inventory-sync service, it can be configured to automatically roll back by reverting the git commit. The Tekton pipeline can include a final notification step that queries the ArgoCD API to verify the sync completed successfully, closing the feedback loop. Secrets management requires careful coordination because the GitOps repository must not contain plaintext secrets. Solutions include Sealed Secrets, External Secrets Operator, or HashiCorp Vault with the ArgoCD Vault plugin, where secret references in the GitOps repository are resolved at deployment time. For progressive delivery of the user-auth-service, ArgoCD integrates with Argo Rollouts to perform canary or blue-green deployments, with Tekton triggering the initial rollout and ArgoCD managing the progressive traffic shifting based on metric analysis from Prometheus.
Code Example
# Tekton Task that updates the GitOps deployment repository after a successful build
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: update-gitops-repo
spec:
params:
- name: IMAGE_DIGEST
description: The digest of the newly built container image
- name: SERVICE_NAME
description: The name of the service being deployed
- name: ENVIRONMENT
description: Target environment for the deployment
steps:
- name: clone-deployment-repo
image: alpine/git:latest
script: |
# Clone the GitOps deployment repository
git clone https://$(cat /workspace/git-credentials/username):$(cat /workspace/git-credentials/password)@github.com/acme-corp/deployment-manifests.git /workspace/deploy-repo
# Navigate to the service overlay directory for the target environment
cd /workspace/deploy-repo/services/$(params.SERVICE_NAME)/overlays/$(params.ENVIRONMENT)
# Update the image tag in the kustomization.yaml file
kustomize edit set image registry.company.com/$(params.SERVICE_NAME)=registry.company.com/$(params.SERVICE_NAME)@$(params.IMAGE_DIGEST)
# Configure git identity for the commit
git config user.email "[email protected]"
# Set the git username for attribution
git config user.name "Tekton CI Pipeline"
# Stage and commit the image tag update
git add -A && git commit -m "deploy: $(params.SERVICE_NAME) $(params.ENVIRONMENT) image $(params.IMAGE_DIGEST)"
# Push the updated manifests to trigger ArgoCD sync
git push origin main
# ArgoCD Application resource for payments-api production deployment
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: payments-api-production
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/acme-corp/deployment-manifests.git
targetRevision: main
path: services/payments-api/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: payments-production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
# Verify ArgoCD sync status from the Tekton pipeline notification step
argocd app get payments-api-production --output json | jq '.status.sync.status'
# Check the health of the deployed application after sync
argocd app get payments-api-production --output json | jq '.status.health.status'Interview Tip
A junior engineer typically describes the Tekton-ArgoCD integration as simply updating an image tag in a repository. When answering this question, emphasize the security separation: Tekton holds source code and build credentials while ArgoCD holds cluster deployment credentials, and neither needs the other's access. Explain the deployment repository structure using Kustomize overlays or Helm values files per environment, and how Tekton targets specific overlays for environment promotion. Discuss the feedback loop where Tekton verifies ArgoCD sync status after pushing the manifest update, and how ArgoCD health checks can trigger automatic rollback by reverting the git commit. Address secrets management in the GitOps repository using Sealed Secrets or External Secrets Operator. If you can describe a production scenario where the clean CI/CD separation prevented a security incident, such as a compromised build system being unable to directly modify production deployments because it lacked cluster credentials, that demonstrates deep understanding of GitOps security principles.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────────┐ │ Tekton + ArgoCD GitOps Integration │ │ │ │ ┌──────────┐ ┌─────────────────────────┐ │ │ │Developer │───→│ Source Repo │ │ │ │git push │ │ payments-api/src │ │ │ └──────────┘ └────────────┬────────────┘ │ │ │ webhook │ │ ↓ │ │ ┌────────────────────────┐ │ │ │ Tekton Pipeline │ │ │ │ ● Clone source │ │ │ │ ● Run tests │ │ │ │ ● Build image │ │ │ │ ● Push to registry │ │ │ │ ● Update GitOps repo │ │ │ └────────────┬───────────┘ │ │ │ git push │ │ ↓ │ │ ┌────────────────────────┐ │ │ │ GitOps Deploy Repo │ │ │ │ ├── base/ │ │ │ │ ├── overlays/dev/ │ │ │ │ ├── overlays/staging/ │ │ │ │ └── overlays/prod/ │ │ │ └────────────┬───────────┘ │ │ │ watch/poll │ │ ↓ │ │ ┌────────────────────────┐ │ │ │ ArgoCD │ │ │ │ ● Detect drift │ │ │ │ ● Sync manifests │ │ │ │ ● Health checks │ │ │ │ ● Auto-rollback │ │ │ └────────────┬───────────┘ │ │ │ deploy │ │ ↓ │ │ ┌────────────────────────┐ │ │ │ Kubernetes Cluster │ │ │ │ ✓ payments-api v2.3 │ │ │ │ ✓ order-processing │ │ │ │ ✓ checkout-service │ │ │ └────────────────────────┘ │ └──────────────────────────────────────────────────────────┘
💬 Comments
Quick Answer
Custom Tekton interceptors are HTTP services that sit in the EventListener processing chain, receiving incoming webhook payloads before they reach TriggerBindings. They can validate, filter, transform, and enrich webhook data by adding custom fields, querying external systems, or enforcing security policies. Interceptors are deployed as Kubernetes services and referenced in EventListener trigger specifications using the webhook interceptor type with a target service URL.
Detailed Answer
Tekton Triggers provides a powerful event processing pipeline consisting of EventListeners, Interceptors, TriggerBindings, and TriggerTemplates. While the built-in interceptors for GitHub, GitLab, Bitbucket, and CEL expression filtering handle common scenarios, enterprise environments frequently require custom business logic that cannot be expressed through simple payload filtering. Custom interceptors fill this gap by providing a programmable extension point where arbitrary code can inspect, validate, transform, and enrich incoming webhook payloads before they trigger pipeline runs. This capability transforms Tekton from a simple webhook-to-pipeline mapper into a sophisticated event processing platform.
A custom interceptor is an HTTP service that implements a simple contract: it receives a POST request with the original webhook payload and trigger context in the body, performs its custom logic, and returns a modified payload with optional additional fields in the extensions map. When the checkout-service repository sends a push webhook to the Tekton EventListener, the request passes through the interceptor chain in order. The built-in GitHub interceptor first validates the webhook signature using the shared secret, confirming the request genuinely originated from GitHub. Then the custom interceptor receives the validated payload and executes business-specific logic. For example, it might query the organization's service registry to determine which team owns the checkout-service repository, fetch the deployment configuration from a central configuration management system, check a deployment freeze calendar to determine if builds should proceed or be queued, and enrich the payload with metadata like the service's tier level, required approval gates, and target deployment environments.
Implementing a custom interceptor involves deploying a lightweight HTTP server as a Kubernetes service in the same cluster as the Tekton EventListener. The interceptor service receives the incoming request body containing the original webhook headers, the webhook body, and any extensions added by previous interceptors in the chain. It processes the data and returns a response with an extensions field containing additional key-value pairs that become available to subsequent TriggerBindings. For the payments-api webhook processing, a custom interceptor written in Go or Python might parse the commit messages to extract Jira ticket references, query the Jira API to verify the ticket is in the correct status for deployment, look up the repository in an internal service catalog to determine the build profile such as Java-Maven versus Node-npm versus Go-modules, and add these enrichments as extensions that the TriggerTemplate uses to parameterize the pipeline run with the correct build steps and deployment targets.
The interceptor chain ordering is critical for both security and correctness. The recommended pattern places the built-in platform interceptor first for webhook signature validation, followed by a CEL interceptor for lightweight filtering such as ignoring pushes to documentation-only branches, and then the custom interceptor for business logic enrichment. This ordering ensures that the custom interceptor only processes authenticated, pre-filtered events, reducing the attack surface and processing load. For the order-processing-service and inventory-sync pipelines, the custom interceptor can implement conditional pipeline selection: examining the changed file paths in the push event to determine whether a full build pipeline, a documentation-only pipeline, or a database-migration-only pipeline should be triggered, routing each to a different TriggerTemplate.
Production deployment of custom interceptors requires attention to reliability, observability, and security. The interceptor service should be deployed with multiple replicas behind a Kubernetes service for high availability, because a failing interceptor blocks all pipeline triggers. Health check endpoints allow Kubernetes to detect and replace unhealthy interceptor pods. Structured logging with request tracing enables debugging when webhooks fail to trigger expected pipeline runs. The interceptor should implement timeouts for external API calls to prevent a slow Jira API response from blocking the entire event processing chain. Rate limiting protects the interceptor from webhook storms when the user-auth-service repository receives a burst of push events during a large merge operation. Caching of external API responses such as service catalog lookups and team ownership data reduces latency and external dependency on every webhook invocation.
Code Example
# Custom interceptor deployment as a Kubernetes service
apiVersion: apps/v1
kind: Deployment
metadata:
name: custom-webhook-interceptor
namespace: tekton-pipelines
spec:
replicas: 2
selector:
matchLabels:
app: custom-webhook-interceptor
template:
metadata:
labels:
app: custom-webhook-interceptor
spec:
containers:
- name: interceptor
image: registry.company.com/tekton/custom-interceptor:v1.4.0
ports:
- containerPort: 8080
env:
- name: JIRA_API_URL
value: "https://jira.company.com/rest/api/2"
- name: SERVICE_CATALOG_URL
value: "https://catalog.internal.company.com/api/v1"
livenessProbe:
httpGet:
path: /healthz
port: 8080
---
# Expose the interceptor as a ClusterIP service for the EventListener
apiVersion: v1
kind: Service
metadata:
name: custom-webhook-interceptor
namespace: tekton-pipelines
spec:
selector:
app: custom-webhook-interceptor
ports:
- port: 80
targetPort: 8080
---
# EventListener with chained interceptors including the custom one
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: payments-api-listener
namespace: tekton-pipelines
spec:
triggers:
- name: payments-push-trigger
interceptors:
- ref:
name: github
params:
- name: secretRef
value:
secretName: github-webhook-secret
secretKey: token
- ref:
name: cel
params:
- name: filter
value: "body.ref.startsWith('refs/heads/') && !body.ref.endsWith('/docs')"
- webhook:
objectRef:
kind: Service
name: custom-webhook-interceptor
namespace: tekton-pipelines
bindings:
- ref: payments-api-binding
template:
ref: payments-api-template
# Python custom interceptor handler (main.py)
# from flask import Flask, request, jsonify
# import requests
# app = Flask(__name__)
# @app.route('/', methods=['POST'])
# def intercept():
# body = request.json # Receive the webhook payload from EventListener
# repo_name = body['body']['repository']['full_name'] # Extract repository name
# catalog_resp = requests.get(f"{CATALOG_URL}/services/{repo_name}") # Query service catalog
# service_info = catalog_resp.json() # Parse service metadata
# extensions = { # Build extensions map for TriggerBindings
# 'team_name': service_info['team'], # Owning team
# 'build_profile': service_info['build_type'], # Maven, npm, etc
# 'deploy_tier': service_info['tier'], # Production tier level
# 'freeze_active': check_deploy_freeze(), # Deployment freeze status
# }
# body['extensions'] = extensions # Add extensions to the payload
# return jsonify(body), 200 # Return enriched payload to EventListener
kubectl apply -f custom-interceptor-deployment.yaml
# Verify the interceptor is running and healthy
kubectl get pods -n tekton-pipelines -l app=custom-webhook-interceptorInterview Tip
A junior engineer typically uses only the built-in GitHub or GitLab interceptors and is unaware that custom interceptors exist. When answering this question, explain the interceptor chain architecture: webhooks flow through an ordered sequence of interceptors where each one can validate, filter, transform, or enrich the payload before passing it to the next. Describe a concrete use case such as querying a service catalog to determine the build profile for the incoming repository, checking a deployment freeze calendar, or extracting Jira ticket references from commit messages for compliance tracking. Discuss the extensions map mechanism that custom interceptors use to pass enriched data to TriggerBindings and TriggerTemplates. Address operational concerns including high availability through multiple replicas, timeout handling for external API calls, caching to reduce latency, and structured logging for debugging failed triggers. If you can describe how a custom interceptor solved a real business problem, such as routing different file change patterns to different pipeline templates to avoid running unnecessary full builds, that demonstrates the kind of platform engineering creativity that distinguishes senior engineers.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────────┐
│ Tekton Custom Interceptor Chain │
│ │
│ GitHub/GitLab Webhook │
│ │ │
│ ↓ │
│ ┌──────────────────┐ │
│ │ EventListener │ │
│ └────────┬─────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────┐ Interceptor Chain │
│ │ 1. GitHub │ ● Validate webhook signature │
│ │ Interceptor │ ● Reject unsigned requests │
│ └────────┬─────────┘ │
│ ↓ │
│ ┌──────────────────┐ │
│ │ 2. CEL Filter │ ● Filter: branch refs only │
│ │ Interceptor │ ● Skip: docs-only branches │
│ └────────┬─────────┘ │
│ ↓ │
│ ┌──────────────────┐ ┌─────────────────────┐ │
│ │ 3. Custom │──→│ External APIs │ │
│ │ Interceptor │ │ ● Service Catalog │ │
│ │ (your code) │←──│ ● Jira API │ │
│ │ │ │ ● Freeze Calendar │ │
│ │ extensions: { │ └─────────────────────┘ │
│ │ team, profile, │ │
│ │ tier, freeze │ │
│ │ } │ │
│ └────────┬─────────┘ │
│ ↓ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ TriggerBinding │──→│ TriggerTemplate │ │
│ │ (extract params) │ │ (create Pipeline)│ │
│ └──────────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────────────┘💬 Comments
Quick Answer
Tekton pipeline performance optimization involves workspace-based dependency caching using PersistentVolumeClaims, parallel task execution using runAfter DAG scheduling, right-sizing container resource requests and limits for each step, leveraging Tekton Results for offloading completed run data, and implementing pipeline-level strategies like step-level caching, layer-aware container builds, and intelligent test splitting across parallel TaskRuns.
Detailed Answer
Pipeline performance directly impacts developer productivity and organizational velocity. When the payments-api pipeline takes 25 minutes to complete, developers context-switch to other work, lose flow state, and batch their commits to avoid frequent feedback delays. Reducing that pipeline to 8 minutes through systematic optimization transforms the development experience from asynchronous batch processing to near-interactive feedback. The optimization strategy spans three dimensions: eliminating redundant work through caching, maximizing concurrency through parallel execution, and right-sizing resources to eliminate bottlenecks without wasting cluster capacity.
Dependency caching is the highest-impact optimization for most pipelines. Without caching, every pipeline run for the order-processing-service downloads hundreds of megabytes of Maven dependencies, npm packages, or Go modules from external registries. A PersistentVolumeClaim-backed workspace preserves the dependency cache across pipeline runs. The first run populates the cache, and subsequent runs resolve dependencies from the local cache in seconds rather than minutes. For Maven-based services, the .m2/repository directory is stored on the PVC. For npm, the node_modules directory or npm cache is persisted. The cache workspace is mounted into each task that needs dependencies, and the pipeline includes a cache validation step that checks cache freshness against the lockfile hash. When the lockfile changes, indicating dependency updates, the cache is invalidated and rebuilt. This single optimization typically reduces pipeline duration by 30-50 percent for dependency-heavy projects like the checkout-service that pulls 400+ npm packages.
Parallel task execution exploits the DAG nature of Tekton pipelines to run independent tasks simultaneously. The runAfter field in pipeline task definitions creates explicit dependencies, and any tasks without dependencies on each other execute concurrently. For the user-auth-service pipeline, the unit test task, lint task, and security scan task can all run in parallel after the clone-and-install task completes, since they share no data dependencies beyond the source code workspace. More aggressive parallelism splits a single large test suite into multiple parallel TaskRuns using a fan-out pattern. The test suite is partitioned by module, package, or time-based balancing, with each partition executing as a separate TaskRun. A final fan-in task collects the results and generates a unified test report. This pattern reduces the inventory-sync service's 12-minute test suite to 4 minutes using three parallel test runners.
Container image build optimization addresses the longest-running step in most pipelines. Layer caching with kaniko or buildkit reuses previously built layers when Dockerfile instructions have not changed. Building with a persistent buildkit daemon rather than ephemeral build containers maintains the layer cache across builds, reducing rebuild times from 5 minutes to under 1 minute for incremental changes. Multi-stage builds eliminate unnecessary tools and dependencies from the final image, reducing push time. For services with multiple components like the payments-api that includes both a Java backend and a React frontend, the backend and frontend builds execute as parallel tasks, with a final task that combines the artifacts into the deployable image.
Resource management optimization ensures that pipeline tasks request appropriate CPU and memory to avoid both throttling and waste. Under-provisioned tasks run slowly due to CPU throttling or get OOM-killed, causing pipeline failures that require full reruns. Over-provisioned tasks waste cluster capacity that could serve other pipeline runs. The optimization process involves running pipelines with resource metrics collection enabled, analyzing actual resource consumption using Prometheus metrics from the kubelet, and adjusting requests and limits to match the observed profile with reasonable headroom. For the order-processing-service pipeline, the compile step might need 2 CPU cores and 4Gi memory for 90 seconds, while the unit test step needs 1 CPU and 2Gi for 3 minutes. Setting these precisely allows the Kubernetes scheduler to bin-pack pipeline pods efficiently, increasing the number of concurrent pipeline runs the cluster can support.
Pipeline-level structural optimizations reduce overhead beyond individual task tuning. Combining multiple small steps into a single step within a task eliminates pod scheduling overhead, since each step in a Tekton task shares the same pod but each task in a pipeline creates a new pod. Pod scheduling adds 5-15 seconds of overhead, so consolidating 6 sequential single-step tasks into 2 multi-step tasks saves 20-60 seconds. Tekton Results offloads completed PipelineRun and TaskRun records from etcd to a database, reducing API server load and improving controller reconciliation speed as the cluster scales to thousands of daily pipeline runs. Finally, workspace volume optimization matters: using emptyDir volumes for ephemeral data and PVCs only for persistent caches avoids the volume provisioning and attachment delays that occur with dynamically provisioned persistent volumes in cloud environments.
Code Example
# Pipeline with parallel tasks and dependency caching via PVC workspace
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: payments-api-optimized
spec:
workspaces:
- name: source
description: Shared source code workspace
- name: maven-cache
description: Persistent Maven dependency cache
tasks:
- name: clone
taskRef:
name: git-clone
workspaces:
- name: output
workspace: source
- name: unit-tests
taskRef:
name: maven-test
runAfter: [clone]
workspaces:
- name: source
workspace: source
- name: maven-repo
workspace: maven-cache
- name: lint
taskRef:
name: checkstyle-lint
runAfter: [clone]
workspaces:
- name: source
workspace: source
- name: security-scan
taskRef:
name: trivy-scan
runAfter: [clone]
workspaces:
- name: source
workspace: source
- name: build-image
taskRef:
name: kaniko-build
runAfter: [unit-tests, lint, security-scan]
workspaces:
- name: source
workspace: source
---
# PipelineRun with PVC for persistent caching across runs
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: payments-api-run-42
spec:
pipelineRef:
name: payments-api-optimized
workspaces:
- name: source
volumeClaimTemplate:
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 2Gi
- name: maven-cache
persistentVolumeClaim:
claimName: maven-cache-pvc
---
# Task with right-sized resource requests for efficient scheduling
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: maven-test
spec:
steps:
- name: run-tests
image: maven:3.9-eclipse-temurin-17
command: ["mvn"]
args: ["test", "-Dmaven.repo.local=/workspace/maven-repo"]
resources:
requests:
cpu: "2"
memory: 4Gi
limits:
cpu: "4"
memory: 6Gi
# Create the persistent cache PVC once per namespace
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: maven-cache-pvc
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 10Gi
EOF
# Monitor pipeline execution time trends using tkn CLI
tkn pipelinerun list --limit 20 -o json | jq '[.items[] | {name: .metadata.name, duration: .status.completionTime}]'
# Check resource consumption of pipeline pods for right-sizing
kubectl top pods -l tekton.dev/pipeline=payments-api-optimizedInterview Tip
A junior engineer typically describes pipeline optimization as simply adding more resources or parallelizing everything. When answering this question, present a structured optimization framework across three dimensions: caching to eliminate redundant work, parallelism to exploit concurrency, and resource right-sizing to remove bottlenecks. Quantify the impact of each optimization: dependency caching typically saves 30-50 percent of pipeline duration, parallel test execution provides near-linear speedup with the number of runners, and build layer caching reduces incremental rebuilds from minutes to seconds. Discuss the tradeoffs of each approach, such as PVC-based caching requiring ReadWriteMany access modes for concurrent pipeline runs or emptyDir caching being lost when pods are rescheduled. Explain the pod scheduling overhead of Tekton's one-pod-per-task model and the optimization of consolidating sequential single-step tasks into multi-step tasks. If you can share specific before-and-after metrics from a pipeline optimization effort, such as reducing the payments-api pipeline from 25 minutes to 8 minutes through a combination of Maven caching, parallel test splitting, and kaniko layer caching, that makes the answer memorable and credible.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────────┐ │ Optimized Pipeline Execution (DAG View) │ │ │ │ Time ──→ │ │ │ │ ┌─────────┐ │ │ │ Clone │ (30s) │ │ └────┬────┘ │ │ │ │ │ ├──────────────────┬──────────────────┐ │ │ ↓ ↓ ↓ │ │ ┌──────────┐ ┌───────────┐ ┌─────────────┐ │ │ │Unit Tests│ │ Lint │ │Security Scan│ │ │ │ (3min) │ │ (1min) │ │ (2min) │ │ │ │ cached │ │ │ │ │ │ │ │ deps ● │ │ │ │ │ │ │ └────┬─────┘ └─────┬─────┘ └──────┬──────┘ │ │ │ │ │ │ │ └────────────┬────┴──────────────────┘ │ │ ↓ │ │ ┌────────────────┐ │ │ │ Build Image │ (1min) │ │ │ kaniko+cache │ │ │ └────────────────┘ │ │ │ │ Without optimization: ──────────────────────── 25 min │ │ With optimization: ────────── 8 min │ │ │ │ Savings: │ │ ● Dependency cache (PVC): -4 min │ │ ● Parallel tasks: -5 min │ │ ● Build layer cache: -4 min │ │ ● Resource right-sizing: -2 min │ │ ● Step consolidation: -2 min │ └──────────────────────────────────────────────────────────┘
💬 Comments
Quick Answer
Tekton RBAC implementation uses Kubernetes Role and ClusterRole resources to control access to Tekton CRDs like Pipeline, Task, PipelineRun, and TaskRun at the namespace level. Combined with ServiceAccount isolation for pipeline execution, PodSecurityStandards for runtime security, and OPA/Kyverno policies for pipeline definition governance, this creates a layered security model where teams can manage their own pipelines without affecting other teams or escalating privileges beyond their namespace boundaries.
Detailed Answer
Securing Tekton in a multi-team environment requires thinking beyond simple access control lists to a comprehensive security architecture that addresses four distinct threat surfaces: who can define pipelines, what those pipelines can do at runtime, how pipeline credentials are scoped, and what governance policies ensure pipeline definitions comply with organizational standards. A developer on the payments-api team should be able to create and run pipelines in their namespace but must not be able to read secrets from the user-auth-service namespace, modify shared ClusterTasks used by all teams, or run pipeline steps with privileged container access that could compromise the node.
Kubernetes RBAC forms the foundation layer. Custom Roles define the specific Tekton API operations each team can perform. A pipeline-developer Role grants create, get, list, and watch permissions on Pipelines, Tasks, PipelineRuns, and TaskRuns within the team's namespace, along with read access to ClusterTasks and ClusterPipelines that provide shared reusable components. A pipeline-admin Role additionally grants update and delete permissions plus the ability to manage EventListeners and TriggerBindings for webhook integrations. A pipeline-viewer Role provides read-only access for stakeholders who need to monitor pipeline status without modification rights. These Roles are bound to team groups through RoleBindings, with the group membership managed by the organization's identity provider through OIDC integration with the Kubernetes API server.
ServiceAccount isolation is the critical runtime security boundary. Each team's PipelineRuns execute under a team-specific ServiceAccount rather than a shared or default service account. The checkout-service team's ServiceAccount has an ImagePullSecret for their team's registry namespace, a Secret containing their deployment credentials for the staging environment, and RBAC permissions scoped to their namespace. When a PipelineRun executes, the TaskRun pods inherit the ServiceAccount's identity, and any Kubernetes API calls from within the pipeline steps are authorized against that ServiceAccount's RBAC bindings. This prevents a malicious or misconfigured pipeline step from accessing resources outside the team's boundary. Critical secret isolation ensures that the payments-api team's database credentials are stored in their namespace and accessible only to their ServiceAccount, while the order-processing-service team cannot reference those secrets even if they know the secret name.
Policy engines like OPA Gatekeeper or Kyverno provide governance over pipeline definitions that RBAC alone cannot enforce. While RBAC controls who can create a Pipeline resource, policy engines control what that Pipeline can contain. Policies can enforce that all TaskRun steps must use images from an approved internal registry rather than arbitrary public images, that no pipeline step can run as root or with privileged security context, that all PipelineRuns must include a security scanning task before the image build task, that resource requests and limits must be specified for every step to prevent resource exhaustion, and that all pipeline workspaces must use volumeClaimTemplates rather than hostPath volumes. These policies are evaluated at admission time when the Pipeline or PipelineRun resource is created, rejecting non-compliant definitions with a descriptive error message before they ever execute.
Audit logging and monitoring complete the security architecture. Kubernetes audit logs capture every Tekton API operation with the authenticated identity, providing a compliance-ready trail of who created, modified, or triggered each pipeline. Prometheus metrics from the Tekton controller track pipeline execution patterns, and alerting rules flag anomalies such as pipeline runs executing at unusual hours, pipelines accessing resources outside their normal scope patterns, or sudden increases in failed pipeline runs that might indicate credential compromise. For the inventory-sync service pipeline, audit policies can be configured to log every access to production deployment credentials at the RequestResponse level, creating a complete record of when production deployments were triggered and by whom. Integration with SIEM platforms like Splunk or Elastic Security enables correlation of Tekton activity with broader infrastructure security events, detecting scenarios where a compromised developer account attempts to modify pipeline definitions to exfiltrate secrets through build step output logs.
Code Example
# Role for pipeline developers - can create and run pipelines in their namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: tekton-pipeline-developer
namespace: ci-payments-team
rules:
- apiGroups: ["tekton.dev"]
resources: ["pipelines", "tasks", "pipelineruns", "taskruns"]
verbs: ["create", "get", "list", "watch"]
- apiGroups: ["tekton.dev"]
resources: ["clustertasks"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
---
# RoleBinding assigning the developer role to the payments team group
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: payments-team-pipeline-devs
namespace: ci-payments-team
subjects:
- kind: Group
name: payments-team-developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: tekton-pipeline-developer
apiGroup: rbac.authorization.k8s.io
---
# Team-specific ServiceAccount for pipeline execution isolation
apiVersion: v1
kind: ServiceAccount
metadata:
name: payments-pipeline-sa
namespace: ci-payments-team
secrets:
- name: payments-registry-creds
- name: payments-deploy-token
---
# Kyverno policy enforcing approved base images in all pipeline steps
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: tekton-approved-images-only
spec:
validationFailureAction: enforce
rules:
- name: check-task-step-images
match:
resources:
kinds: ["tekton.dev/v1beta1/Task"]
validate:
message: "Task steps must use images from the approved registry"
pattern:
spec:
steps:
- image: "registry.company.com/*"
---
# Kyverno policy preventing privileged containers in pipeline steps
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: tekton-no-privileged-steps
spec:
validationFailureAction: enforce
rules:
- name: deny-privileged-steps
match:
resources:
kinds: ["tekton.dev/v1beta1/TaskRun"]
validate:
message: "Pipeline steps cannot run with privileged security context"
deny:
conditions:
- key: "{{request.object.spec.taskSpec.steps[].securityContext.privileged}}"
operator: Equals
value: true
# Apply all RBAC and policy resources to the cluster
kubectl apply -f tekton-rbac-policies/
# Verify that the payments team can create PipelineRuns in their namespace
kubectl auth can-i create pipelineruns --namespace ci-payments-team --as-group payments-team-developers
# Verify that the payments team cannot access secrets in another namespace
kubectl auth can-i get secrets --namespace ci-checkout-team --as-group payments-team-developersInterview Tip
A junior engineer typically equates Tekton security with Kubernetes RBAC alone, mentioning that teams get namespace-scoped roles. When answering this question, present the four-layer security model: RBAC controls who can define and trigger pipelines, ServiceAccount isolation controls what runtime credentials pipelines can access, policy engines like Kyverno or OPA Gatekeeper control what pipeline definitions can contain, and audit logging provides the accountability trail. Emphasize the distinction between RBAC (who can create a Pipeline resource) and policy governance (what that Pipeline is allowed to do, such as which images it can use or whether steps can run as root). Discuss ServiceAccount isolation as the critical runtime boundary, explaining how team-specific ServiceAccounts with scoped secrets prevent cross-team credential access even within a shared cluster. If you can describe a scenario where a policy engine caught a non-compliant pipeline definition before it executed, such as a developer inadvertently referencing a public Docker Hub image that violated the organization's approved-registry policy, that demonstrates practical security governance experience.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────────┐ │ Tekton Security Architecture (4 Layers) │ │ │ │ Layer 1: RBAC (Who can act) │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Role: pipeline-developer Role: pipeline-admin │ │ │ │ ● create/get PipelineRuns ● + update/delete │ │ │ │ ● create/get Tasks ● + manage Triggers │ │ │ │ ● read ClusterTasks ● + manage EventListenr │ │ │ │ │ │ │ │ RoleBinding → Team Group (OIDC) │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Layer 2: ServiceAccount Isolation (Runtime boundary) │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ payments-pipeline-sa checkout-pipeline-sa │ │ │ │ ● payments-registry-creds ● checkout-registry-creds│ │ │ │ ● payments-deploy-token ● checkout-deploy-token │ │ │ │ ✗ Cannot access other NS ✗ Cannot access other NS │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Layer 3: Policy Engine (What pipelines can contain) │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Kyverno/OPA Policies: │ │ │ │ ● Approved registry images only │ │ │ │ ● No privileged containers │ │ │ │ ● Resource limits required │ │ │ │ ● Security scan task mandatory │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Layer 4: Audit & Monitoring (Accountability) │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ ● K8s audit logs → SIEM │ │ │ │ ● Tekton metrics → Prometheus/Grafana │ │ │ │ ● Anomaly alerting on unusual pipeline activity │ │ │ └─────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘
💬 Comments
Quick Answer
Tekton pipeline-as-code stores Pipeline, Task, and Trigger definitions as YAML files within the application's Git repository, versioned alongside the source code. The Tekton Pipelines-as-Code controller watches for changes in a designated directory like .tekton/ and automatically creates PipelineRuns when pull requests or pushes occur. This ensures pipeline definitions evolve with the codebase, enables code review for CI/CD changes, and provides full auditability through Git history.
Detailed Answer
Pipeline-as-code is the practice of storing CI/CD pipeline definitions in the same version-controlled repository as the application source code, treating them as first-class software artifacts subject to the same review, testing, and versioning processes as the application itself. Tekton's pipeline-as-code implementation, through the Pipelines-as-Code component, extends this concept by providing a controller that automatically discovers, validates, and executes pipeline definitions from repository directories when triggered by Git events. This eliminates the disconnect between the application code and the pipeline that builds it, a problem that plagues organizations where pipeline definitions live in a separate central repository and drift out of sync with the applications they serve.
The Pipelines-as-Code controller integrates with GitHub, GitLab, and Bitbucket through webhook events and GitHub App installations. When a developer on the payments-api team creates a pull request, the controller detects the .tekton/ directory in the repository, reads the PipelineRun definitions from that directory, resolves any Task references using in-repo definitions or Tekton Hub references, and creates the PipelineRun in the target namespace. The pipeline runs against the pull request branch, and the results are reported back to the Git provider as commit status checks. This means the pipeline definition in the pull request branch is the one that executes, allowing developers to modify both their application code and the pipeline in the same pull request and see the combined effect immediately. For the checkout-service team, updating a Dockerfile and simultaneously modifying the build pipeline to accommodate the new multi-stage build structure happens in a single reviewable, testable pull request.
Version control of pipeline definitions provides capabilities that centrally managed pipelines cannot match. Every pipeline change has a Git commit with an author, timestamp, reviewer, and description of why the change was made. If the order-processing-service pipeline starts failing after a recent change, git log on the .tekton/ directory immediately reveals what changed, who changed it, and the associated code review discussion. Rolling back a pipeline change is a standard git revert. Branching allows the inventory-sync team to experiment with pipeline changes on a feature branch without affecting the main pipeline. Release tagging pins a specific pipeline version to a release, ensuring reproducible builds even months later. This versioning capability is essential for regulated environments where auditors need to prove that the pipeline used to build a specific release artifact was reviewed and approved through the same change management process as the application code.
The organizational pattern for pipeline-as-code at scale involves a layered template system. A central platform-pipelines repository maintained by the DevOps team contains base Task definitions and Pipeline templates for common technology stacks like Java-Maven, Node-npm, Python-pip, and Go-modules. Application teams reference these shared tasks from their .tekton/ PipelineRun definitions using Tekton Bundles, which package Tasks and Pipelines as OCI artifacts versioned in the container registry. The user-auth-service team's .tekton/pull-request.yaml references the shared maven-build task from the latest approved bundle version, adds service-specific integration test tasks defined locally in the repository, and customizes parameters like the deployment target and notification channels. When the platform team releases a new version of the shared tasks with security improvements, application teams update the bundle reference in their .tekton/ definitions through standard pull requests, ensuring controlled adoption with review and testing.
Operational considerations for pipeline-as-code include security boundaries, resource management, and debugging workflows. The Pipelines-as-Code controller must validate that pipeline definitions in untrusted pull requests from external contributors do not reference privileged resources or attempt to exfiltrate secrets. The controller supports a policy where pull requests from non-collaborators require an explicit comment like /ok-to-test from a repository maintainer before the pipeline executes, preventing untrusted code from running in the CI environment. The .tekton/ directory can contain multiple PipelineRun definitions for different events: pull-request.yaml for pull request validation, push-main.yaml for main branch builds, and release.yaml for tagged releases. Each definition specifies its trigger conditions through annotations, allowing fine-grained control over which pipeline runs for which event. Debugging pipeline failures uses the standard Tekton tools like tkn pipelinerun logs with the addition of being able to reproduce any historical pipeline run by checking out the corresponding Git commit and applying the .tekton/ definitions manually.
Code Example
# .tekton/pull-request.yaml - PipelineRun triggered on pull requests
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: payments-api-pr-run
annotations:
pipelinesascode.tekton.dev/on-event: "[pull_request]"
pipelinesascode.tekton.dev/on-target-branch: "[main]"
pipelinesascode.tekton.dev/max-keep-runs: "5"
pipelinesascode.tekton.dev/task: "[git-clone]"
spec:
pipelineSpec:
workspaces:
- name: source
tasks:
- name: fetch-repo
taskRef:
name: git-clone
kind: ClusterTask
workspaces:
- name: output
workspace: source
params:
- name: url
value: "{{ repo_url }}"
- name: revision
value: "{{ revision }}"
- name: run-tests
runAfter: [fetch-repo]
taskSpec:
workspaces:
- name: source
steps:
- name: test
image: maven:3.9-eclipse-temurin-17
workingDir: /workspace/source
script: |
# Run the full unit test suite with coverage reporting
mvn test -Dmaven.repo.local=/workspace/source/.m2
# Generate the coverage report for Code Insights
mvn jacoco:report
workspaces:
- name: source
workspace: source
workspaces:
- name: source
volumeClaimTemplate:
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 1Gi
# .tekton/push-main.yaml - PipelineRun triggered on pushes to main
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: payments-api-push-main
annotations:
pipelinesascode.tekton.dev/on-event: "[push]"
pipelinesascode.tekton.dev/on-target-branch: "[main]"
pipelinesascode.tekton.dev/task: "[git-clone, kaniko]"
pipelinesascode.tekton.dev/task-1: "[https://raw.githubusercontent.com/tektoncd/catalog/main/task/kaniko/0.6/kaniko.yaml]"
spec:
pipelineSpec:
workspaces:
- name: source
- name: docker-credentials
tasks:
- name: fetch-repo
taskRef:
name: git-clone
kind: ClusterTask
workspaces:
- name: output
workspace: source
params:
- name: url
value: "{{ repo_url }}"
- name: revision
value: "{{ revision }}"
- name: build-and-push
runAfter: [fetch-repo]
taskRef:
name: kaniko
kind: ClusterTask
workspaces:
- name: source
workspace: source
- name: dockerconfig
workspace: docker-credentials
params:
- name: IMAGE
value: "registry.company.com/payments-api:{{ revision }}"
workspaces:
- name: source
volumeClaimTemplate:
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 1Gi
- name: docker-credentials
secret:
secretName: registry-credentials
# Install the Pipelines-as-Code controller
kubectl apply -f https://raw.githubusercontent.com/openshift-pipelines/pipelines-as-code/stable/release.yaml
# Create a GitHub App and configure the Pipelines-as-Code controller
tkn pac bootstrap github-app
# List all active PipelineRuns created by Pipelines-as-Code
tkn pac list -n ci-payments-teamInterview Tip
A junior engineer typically describes pipeline-as-code as simply storing YAML files in a repository without understanding the operational implications. When answering this question, emphasize three key advantages over centrally managed pipelines: atomic changes where application code and pipeline modifications are reviewed and tested together in a single pull request, full Git history providing audit trail and rollback capability for pipeline definitions, and branch-specific pipeline execution where the pipeline in the feature branch is the one that runs, enabling pipeline experimentation without affecting other developers. Discuss the Tekton Pipelines-as-Code controller's role in discovering .tekton/ definitions, resolving task references, creating PipelineRuns, and reporting status back to the Git provider. Address the security concern of untrusted pull requests by explaining the ok-to-test approval mechanism. If you have implemented a layered template system using Tekton Bundles where a platform team maintains shared tasks and application teams compose them in their repository-local pipeline definitions, that demonstrates pipeline platform engineering at a level that distinguishes staff engineers from senior engineers.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────────┐ │ Pipeline-as-Code with Tekton │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ payments-api Repository │ │ │ │ ├── src/ │ │ │ │ │ ├── main/java/... │ │ │ │ │ └── test/java/... │ │ │ │ ├── Dockerfile │ │ │ │ ├── pom.xml │ │ │ │ └── .tekton/ │ │ │ │ ├── pull-request.yaml │ │ │ │ ├── push-main.yaml │ │ │ │ └── release.yaml │ │ │ └──────────────────┬──────────────────┘ │ │ │ PR / Push event │ │ ↓ │ │ ┌──────────────────────────────────────┐ │ │ │ Pipelines-as-Code Controller │ │ │ │ ● Discover .tekton/ definitions │ │ │ │ ● Resolve Task references │ │ │ │ ● Validate against policies │ │ │ │ ● Create PipelineRun │ │ │ └──────────────────┬───────────────────┘ │ │ │ │ │ ┌───────────┴───────────┐ │ │ ↓ ↓ │ │ ┌──────────────┐ ┌────────────────┐ │ │ │ PipelineRun │ │ Status Report │ │ │ │ ● Clone │ │ → GitHub Check │ │ │ │ ● Test │ │ ✓ passed │ │ │ │ ● Build │ │ ✗ failed │ │ │ │ ● Deploy │ │ │ │ │ └──────────────┘ └────────────────┘ │ │ │ │ ● Pipeline versioned with app code in Git │ │ ● Changes reviewed in same PR as code changes │ │ ● Branch-specific pipeline execution │ │ ● Full audit trail via git log .tekton/ │ └──────────────────────────────────────────────────────────┘
💬 Comments
Quick Answer
Migrating from Jenkins to Tekton involves mapping Jenkins concepts to Tekton equivalents: Jenkinsfile stages become Tekton Tasks within a Pipeline, shared libraries become reusable ClusterTasks or Tekton Bundles, Jenkins credentials become Kubernetes Secrets mounted in ServiceAccounts, and Jenkins plugins become container images with equivalent tools. The migration is executed incrementally, running both systems in parallel during the transition with a compatibility layer that routes builds to either system based on migration status.
Detailed Answer
Migrating from Jenkins to Tekton is one of the most common enterprise CI/CD modernization efforts, and it is far more complex than a syntax translation exercise. Jenkins represents a fundamentally different paradigm: a long-running stateful server with an imperative scripting model, extensive plugin ecosystem, and decades of accumulated organizational knowledge embedded in Groovy shared libraries and job configurations. Tekton represents the cloud-native paradigm: stateless, declarative, Kubernetes-native, with each pipeline run as an isolated pod lifecycle. The migration requires not just rewriting pipelines but rethinking how the organization approaches CI/CD, retraining teams, and establishing new operational patterns. Organizations that attempt a big-bang migration by converting all Jenkins pipelines simultaneously invariably fail. The successful pattern is incremental migration with parallel operation.
The conceptual mapping between Jenkins and Tekton forms the foundation of migration planning. A Jenkins Declarative Pipeline with stages maps to a Tekton Pipeline with Tasks. Each stage becomes a Task, and the stage's steps become the Task's steps. A Jenkins Scripted Pipeline's arbitrary Groovy logic requires decomposition into discrete Tasks with well-defined inputs and outputs. Jenkins shared libraries, which encapsulate reusable pipeline logic across the payments-api, order-processing-service, and checkout-service, map to Tekton ClusterTasks or Tasks packaged as Tekton Bundles in an OCI registry. Jenkins credentials stored in the credential manager map to Kubernetes Secrets referenced by ServiceAccounts or mounted as workspace volumes. Jenkins agent labels that route builds to specific node types map to Tekton TaskRun nodeSelector or affinity rules. Jenkins parameterized builds map to Tekton Pipeline params. Jenkins post-build actions and notifications map to Tekton finally tasks that execute regardless of pipeline success or failure.
The incremental migration strategy begins with an assessment phase where all Jenkins jobs are catalogued by complexity, frequency, and business criticality. Simple jobs like the inventory-sync nightly build with straightforward clone-test-build-deploy stages are migrated first to build team experience. Complex jobs like the payments-api multi-branch pipeline with 15 stages, custom Groovy logic, parallel test execution, and integration test environments are migrated last after the team has established Tekton patterns and reusable task libraries. During the parallel operation phase, both Jenkins and Tekton run simultaneously. A routing layer, implemented as a webhook proxy or Git-provider webhook configuration, directs events for migrated repositories to Tekton EventListeners while unmigrated repositories continue triggering Jenkins. This allows team-by-team migration at each team's pace rather than a forced cutover.
The most challenging aspect of Jenkins-to-Tekton migration is replacing Jenkins plugins with container-based equivalents. Jenkins has over 1800 plugins covering every conceivable integration: SonarQube analysis, Slack notifications, Jira ticket updates, AWS deployments, Artifactory publishing, and hundreds more. Each plugin used in the organization's Jenkins pipelines must be replaced with either a Tekton Catalog task from the Tekton Hub, a custom Task that runs the equivalent CLI tool in a container image, or a purpose-built container image that packages the tool with the required configuration. For example, the Jenkins SonarQube plugin that the user-auth-service pipeline uses becomes a Tekton Task running the sonar-scanner CLI in the sonarsource/sonar-scanner-cli container image. The Jenkins Slack notification plugin becomes a Tekton Task that calls the Slack API directly using curl or a lightweight notification container. This container-based approach is actually more transparent and portable than Jenkins plugins, but the initial migration effort for organizations using 30-50 plugins is substantial.
Post-migration operational differences require organizational adaptation. Jenkins provides a centralized web UI where managers and release engineers monitor all pipeline activity across the organization. Tekton's native interface is the Kubernetes API accessed through kubectl or the tkn CLI, which is less accessible to non-technical stakeholders. The Tekton Dashboard or OpenShift Pipelines console provides a web UI, but it is simpler than Jenkins and may require customization to match the organization's monitoring needs. Jenkins's build history and artifact archival are built-in features, while Tekton requires Tekton Results for long-term run history and explicit artifact publishing to registries or storage systems. Team training must address not just Tekton syntax but Kubernetes concepts like pods, services, persistent volumes, and RBAC that are prerequisites for understanding how Tekton works. Organizations that underinvest in training find that teams revert to writing shell scripts inside Tekton steps that recreate Jenkins patterns rather than embracing the declarative cloud-native model.
Code Example
# Jenkins Declarative Pipeline (before migration)
# pipeline {
# agent { label 'maven' }
# stages {
# stage('Checkout') { steps { checkout scm } }
# stage('Test') { steps { sh 'mvn test' } }
# stage('Build') { steps { sh 'mvn package -DskipTests' } }
# stage('Docker') { steps {
# sh 'docker build -t registry.company.com/payments-api:${BUILD_NUMBER} .'
# sh 'docker push registry.company.com/payments-api:${BUILD_NUMBER}'
# }}
# stage('Deploy') { steps { sh 'kubectl apply -f k8s/' } }
# }
# post { always { slackSend channel: '#ci-cd', message: "Build ${currentBuild.result}" } }
# }
# Equivalent Tekton Pipeline (after migration)
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: payments-api-pipeline
spec:
params:
- name: repo-url
description: Git repository URL for payments-api
- name: image-tag
description: Container image tag for the build
workspaces:
- name: shared-workspace
- name: docker-credentials
tasks:
- name: clone
taskRef:
name: git-clone
kind: ClusterTask
params:
- name: url
value: $(params.repo-url)
workspaces:
- name: output
workspace: shared-workspace
- name: test
taskRef:
name: maven
kind: ClusterTask
runAfter: [clone]
params:
- name: GOALS
value: ["test"]
workspaces:
- name: source
workspace: shared-workspace
- name: build-image
taskRef:
name: kaniko
kind: ClusterTask
runAfter: [test]
params:
- name: IMAGE
value: "registry.company.com/payments-api:$(params.image-tag)"
workspaces:
- name: source
workspace: shared-workspace
- name: dockerconfig
workspace: docker-credentials
- name: deploy
taskRef:
name: kubernetes-actions
kind: ClusterTask
runAfter: [build-image]
params:
- name: script
value: "kubectl apply -f /workspace/source/k8s/"
workspaces:
- name: source
workspace: shared-workspace
finally:
- name: notify-slack
taskRef:
name: send-to-webhook-slack
params:
- name: webhook-secret
value: slack-webhook-secret
- name: message
value: "Pipeline $(context.pipelineRun.name) completed with status $(tasks.status)"
# Jenkins concept mapping reference
# Jenkins agent label → Tekton TaskRun nodeSelector
# Jenkins credentials → Kubernetes Secrets + ServiceAccount
# Jenkins shared library → ClusterTask or Tekton Bundle
# Jenkins parameters → Pipeline params
# Jenkins post actions → Pipeline finally tasks
# Jenkins parallel stages → Tasks without runAfter dependencies
# Trigger the migrated pipeline with the same parameters as Jenkins
tkn pipeline start payments-api-pipeline --param repo-url=https://github.com/acme-corp/payments-api.git --param image-tag=build-142 --workspace name=shared-workspace,volumeClaimTemplateFile=pvc-template.yaml --workspace name=docker-credentials,secret=registry-credentials
# Compare build times between Jenkins and Tekton during parallel operation
tkn pipelinerun list -o json | jq '[.items[] | {name: .metadata.name, duration: (.status.completionTime | . as $end | .status.startTime | . as $start)}]'Interview Tip
A junior engineer typically describes Jenkins-to-Tekton migration as rewriting Jenkinsfiles in YAML. When answering this question, demonstrate understanding of the fundamental paradigm shift: Jenkins is a stateful server with an imperative scripting model and a massive plugin ecosystem, while Tekton is stateless, declarative, and Kubernetes-native with container-based tooling. Present the conceptual mapping: stages become Tasks, shared libraries become ClusterTasks or Tekton Bundles, credentials become Kubernetes Secrets, agent labels become node selectors, and post actions become finally tasks. Emphasize the incremental migration strategy with parallel operation rather than big-bang cutover, explaining how a webhook routing layer directs events to either Jenkins or Tekton based on migration status. Address the plugin replacement challenge, noting that each Jenkins plugin must be replaced with a container-based equivalent. Discuss post-migration operational gaps like centralized monitoring and build history that require Tekton Dashboard and Tekton Results to address. If you have led or participated in an actual Jenkins-to-Tekton migration, describe the assessment process, the order in which pipelines were migrated, the blockers you encountered, and the team training approach. This migration leadership experience is highly valued because it demonstrates both technical depth and organizational change management skills.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────────┐ │ Jenkins to Tekton Migration Strategy │ │ │ │ Phase 1: Assessment │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Catalogue all Jenkins jobs by complexity │ │ │ │ ● Simple (clone→test→build): migrate first │ │ │ │ ● Medium (shared libs, params): migrate second │ │ │ │ ● Complex (Groovy logic, 15+ stages): migrate last │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Phase 2: Parallel Operation │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ Git Webhook ──→ Routing Layer │ │ │ │ │ │ │ │ │ │ ↓ ↓ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ Jenkins │ │ Tekton │ │ │ │ │ │ (unmigrated) │ │ (migrated) │ │ │ │ │ │ │ │ │ │ │ │ │ │ order-proc │ │ payments-api │ │ │ │ │ │ user-auth │ │ inventory │ │ │ │ │ └──────────────┘ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Phase 3: Concept Mapping │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Jenkins → Tekton │ │ │ │ ─────────────────────────────────────────────────── │ │ │ │ Declarative Pipeline → Pipeline + Tasks │ │ │ │ Stage → Task │ │ │ │ Shared Library → ClusterTask / Bundle │ │ │ │ Credentials → K8s Secrets + SA │ │ │ │ Agent Label → nodeSelector │ │ │ │ Post Actions → finally tasks │ │ │ │ Plugin (SonarQube) → Container image + Task │ │ │ │ Parallel Stages → Tasks without runAfter │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Phase 4: Cutover & Decommission │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ ● All repos migrated → Jenkins decommissioned │ │ │ │ ● Tekton Dashboard for monitoring │ │ │ │ ● Tekton Results for build history retention │ │ │ └─────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘
💬 Comments
Context
Goldman Sachs runs 3,200+ microservices across 40 Kubernetes clusters for their trading platform. Their Jenkins infrastructure required 180 dedicated VMs consuming $2.8M annually, and Jenkins master instability during market-open hours caused build queuing that delayed critical hotfix deployments by up to 45 minutes.
Problem
Goldman Sachs had operated Jenkins as their primary CI/CD platform for over eight years. The Jenkins infrastructure consisted of 12 masters and 168 agent VMs, each requiring manual OS patching, JVM tuning, and plugin version management. During peak hours around market open (9:30 AM EST), all 3,200 microservice teams triggered builds simultaneously for overnight changes, overwhelming Jenkins masters and creating build queues of 200+ jobs. The Jenkins masters themselves were single points of failure: when the master serving the trading platform team crashed during a critical production hotfix, the deployment was delayed by 43 minutes while the master was restored from backup. Plugin compatibility was a constant battle: upgrading the Kubernetes plugin to support newer cluster versions often broke the Git plugin or the credentials plugin, creating a fragile dependency chain. Jenkins pipelines were defined in Groovy, which only a handful of platform engineers understood deeply, creating a bottleneck for pipeline modifications. The shared library pattern meant that a bug in a shared pipeline library could break builds for hundreds of teams simultaneously. Security was another concern: Jenkins agents had broad Kubernetes RBAC permissions, and the master stored credentials in an encrypted file on disk rather than integrating natively with Kubernetes secrets or external secret managers.
Solution
Goldman Sachs migrated their CI/CD platform from Jenkins to Tekton, which runs natively on Kubernetes and leverages Kubernetes primitives for scheduling, scaling, and resource management. Each CI/CD pipeline is defined as a set of Tekton Tasks and Pipelines stored as Kubernetes Custom Resources, version-controlled alongside application code. Unlike Jenkins, Tekton has no master node: each PipelineRun creates a set of pods that execute the pipeline steps, then terminates, leaving no long-running infrastructure to maintain. They implemented Tekton Triggers to automatically create PipelineRuns from GitHub webhook events, with a TriggerTemplate that parameterized the pipeline based on the branch, repository, and event type. The EventListener deployment was horizontally scaled with an HPA to handle the market-open build surge. For build isolation, each PipelineRun ran in its own Kubernetes namespace with a dedicated ServiceAccount that had minimal RBAC permissions scoped to only the resources that specific pipeline needed. Credentials were sourced from HashiCorp Vault using the Tekton Vault integration, eliminating stored credentials entirely. They created a catalog of reusable Tasks published to an internal TektonHub instance, including tasks for Go builds, Java builds, container scanning with Trivy, Helm deployments, and integration testing. Pipeline definitions were validated at PR time using Tekton's built-in validation webhooks, catching configuration errors before they reached the cluster. They implemented a custom Tekton Results backend that stored PipelineRun outcomes in PostgreSQL, enabling historical build analytics and compliance auditing. The migration was executed incrementally: teams ran Jenkins and Tekton in parallel for two months, comparing build times and success rates before decommissioning their Jenkins jobs.
Commands
kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml # Install Tekton Pipelines
kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml # Install Tekton Triggers
tkn pipeline start build-and-deploy -p repo-url=https://github.com/gs/trading-svc -p image-tag=v2.4.1 --showlog # Start pipeline run
tkn pipelinerun logs build-and-deploy-run-7x4kf -f # Stream logs from a specific pipeline run
kubectl get pipelineruns -l tekton.dev/pipeline=build-and-deploy --sort-by=.status.completionTime # List recent pipeline runs
Outcome
CI/CD infrastructure costs reduced from $2.8M to $640K annually (77% reduction) by eliminating dedicated Jenkins VMs. Build queue times during market-open dropped from 45 minutes to under 3 minutes due to Kubernetes-native horizontal scaling. Zero master node crashes since there is no master to crash. Pipeline modification PRs can now be reviewed by any engineer, not just Groovy specialists.
Lessons Learned
Running CI/CD as ephemeral Kubernetes workloads fundamentally eliminates the infrastructure management burden that plagues traditional CI servers. The parallel migration approach with side-by-side comparison is essential for building team confidence and catching edge cases. Tekton's native Kubernetes RBAC integration provides much stronger security isolation than bolt-on Jenkins security plugins because each pipeline run has its own ServiceAccount with least-privilege permissions.
◈ Architecture Diagram
┌─────────────────────────────────────────────────┐\n│ Before: Jenkins Architecture │\n│ ┌──────────┐ │\n│ │ Jenkins │──→ 168 Agent VMs (always running) │\n│ │ Master │ $2.8M/year │\n│ │ (SPOF) │ │\n│ └──────────┘ │\n└─────────────────────────────────────────────────┘\n ▼\n┌─────────────────────────────────────────────────┐\n│ After: Tekton Architecture │\n│ │\n│ GitHub Webhook │\n│ │ │\n│ ▼ │\n│ ┌────────────────┐ │\n│ │ EventListener │ (HPA-scaled) │\n│ │ + Trigger │ │\n│ └───────┬────────┘ │\n│ ▼ │\n│ ┌─────────────────────────────────────────┐ │\n│ │ PipelineRun (ephemeral pods) │ │\n│ │ ┌──────┐ ┌───────┐ ┌──────┐ ┌─────┐│ │\n│ │ │Clone │→ │ Build │→ │ Scan │→ │Deploy││ │\n│ │ │(Step)│ │(Step) │ │(Step)│ │(Step)││ │\n│ │ └──────┘ └───────┘ └──────┘ └─────┘│ │\n│ └─────────────────────────────────────────┘ │\n│ │ Pods terminate after completion │\n│ ▼ │\n│ ┌────────────────┐ │\n│ │ Tekton Results │ (PostgreSQL) │\n│ └────────────────┘ │\n└─────────────────────────────────────────────────┘
💬 Comments
Context
Spotify deploys 2,400+ microservices across 15 Kubernetes clusters in 5 regions. Their existing CI/CD system used a centralized pipeline server that became a bottleneck and a single point of failure, and pipeline definitions lived in a separate repository from application code, causing constant drift between pipeline configurations and application requirements.
Problem
Spotify's centralized CI/CD platform was a custom-built system that processed all builds through a single control plane. As the engineering organization grew to over 800 engineers, the centralized approach created multiple problems. First, the pipeline server was a bottleneck: deploying changes to the pipeline infrastructure required a maintenance window because all builds would be affected. Second, pipeline definitions were stored in a central repository managed by the platform team, not alongside the application code they built. When a team needed to change their build process, say adding a new test framework or changing the container base image, they had to file a ticket with the platform team and wait for a PR review. This created a two-week average lead time for pipeline modifications. Third, the centralized pipeline server could not natively schedule builds in the same Kubernetes cluster where the application would run, meaning build artifacts had to be transferred between clusters, adding latency and creating reliability issues when inter-cluster networking had problems. Fourth, the system had no concept of pipeline versioning: when the platform team updated a shared pipeline template, all teams received the change immediately, occasionally breaking builds that depended on the previous behavior. During a major incident, a template update that changed the default container registry endpoint broke 340 pipelines simultaneously, requiring a four-hour rollback.
Solution
Spotify deployed Tekton in every Kubernetes cluster, enabling each cluster to execute its own pipelines independently. Pipeline definitions were moved from the central repository into each application's Git repository as Tekton YAML files, making pipelines first-class citizens of the application codebase. They implemented a GitOps workflow using ArgoCD to synchronize Tekton Pipeline and Task definitions from Git to each cluster. When a developer modified a Pipeline YAML in their application repository, ArgoCD detected the change and applied the updated Pipeline CRD to the target cluster within minutes. Tekton Triggers were configured with GitHub webhook EventListeners in each cluster, with an Interceptor chain that validated webhook signatures, filtered events by branch, and enriched the payload with cluster-specific parameters like registry endpoints and deployment namespaces. They built a custom Tekton Task catalog versioning system: shared tasks were published as OCI artifacts to their container registry, tagged with semantic versions. Pipeline definitions referenced tasks by version (e.g., registry.spotify.net/tekton-tasks/golang-build:v2.3.1), ensuring that teams opted into new task versions explicitly rather than receiving automatic updates. For multi-cluster deployments, they created a promotion Pipeline that triggered downstream PipelineRuns in target clusters using Tekton's Custom Tasks with a CloudEvents-based trigger chain. The entire pipeline-as-code approach was enforced through an OPA Gatekeeper policy that required every namespace to have a valid Tekton Pipeline CRD before a Deployment could be created, ensuring no service could be deployed without a defined CI/CD pipeline.
Commands
tkn hub install task git-clone --version 0.9 # Install git-clone task from TektonHub
kubectl apply -f pipeline.yaml # Apply pipeline definition stored alongside app code
tkn pipeline start spotify-build -p git-revision=$(git rev-parse HEAD) -w name=shared-workspace,claimName=build-pvc --showlog # Run pipeline with workspace
argocd app sync tekton-pipelines --project platform # Sync pipeline definitions via GitOps
tkn task list -n build-system | grep -E 'NAME|golang-build|docker-build|trivy-scan' # List available tasks in catalog
Outcome
Pipeline modification lead time dropped from 2 weeks to 30 minutes (from ticket to production). Zero single-point-of-failure incidents since each cluster operates independently. The broken template incident class was eliminated entirely through versioned task references. Build times improved by 22% on average because builds now execute in the same cluster as the deployment target.
Lessons Learned
Pipeline-as-code is only truly effective when pipeline definitions live in the same repository as the application code and go through the same review process. Versioning shared pipeline tasks as OCI artifacts provides the same dependency management rigor that application code gets from package managers. Deploying CI/CD infrastructure in every cluster eliminates cross-cluster dependencies and makes the system resilient to individual cluster failures.
◈ Architecture Diagram
┌──────────────────────────────────────────────────┐\n│ Application Repository │\n│ ┌──────────┐ ┌──────────────┐ ┌─────────────┐ │\n│ │ src/ │ │ pipeline.yaml│ │ Dockerfile │ │\n│ │ main.go │ │ (Tekton) │ │ │ │\n│ └──────────┘ └──────┬───────┘ └─────────────┘ │\n└───────────────────────┼──────────────────────────┘\n │ git push\n ┌─────────┴──────────┐\n ▼ ▼\n ┌──────────────────┐ ┌──────────────────┐\n │ ArgoCD (sync │ │ GitHub Webhook │\n │ Pipeline CRDs) │ │ (trigger build) │\n └────────┬─────────┘ └────────┬─────────┘\n │ │\n ▼ ▼\n ┌──────────────────────────────────────────┐\n │ Kubernetes Cluster (per region) │\n │ ┌────────────┐ ┌───────────────────┐ │\n │ │ Pipeline │ │ EventListener │ │\n │ │ CRDs │ │ + Interceptors │ │\n │ └────────────┘ └────────┬──────────┘ │\n │ ▼ │\n │ ┌──────────────────────────────────┐ │\n │ │ PipelineRun (local build) │ │\n │ │ Clone → Build → Scan → Deploy │ │\n │ └──────────────────────────────────┘ │\n └──────────────────────────────────────────┘
💬 Comments
Context
Red Hat ships container images consumed by thousands of enterprise customers running OpenShift. A supply chain attack on a build dependency in late 2024 exposed gaps in their build provenance verification, triggering an urgent mandate to achieve SLSA Level 3 compliance for all container image builds within 90 days.
Problem
After a supply chain attack compromised a third-party Go module used by several Red Hat container images, the security team discovered that their existing build pipeline provided no cryptographic proof of what source code was used, what build commands were executed, or what dependencies were resolved during the build. Their Jenkins-based pipelines produced container images, but there was no signed attestation linking an image digest to a specific Git commit, build environment, and dependency tree. When customers asked for provenance information after the incident, the team could only provide Jenkins build logs, which were stored on ephemeral agent VMs and were easily tampered with, making them unsuitable as evidence for supply chain integrity. The SLSA (Supply-chain Levels for Software Artifacts) framework defines four levels of build integrity, with Level 3 requiring that builds run on a hardened, isolated build platform that generates non-forgeable provenance attestations. Their Jenkins setup could not meet this requirement because Jenkins agents had mutable filesystems, shared build caches across jobs, and no mechanism for generating signed build provenance. The security mandate required SLSA Level 3 compliance for all 450 container images shipped to customers within 90 days. Manual attestation generation was not scalable, and any solution needed to be transparent to development teams so it would not slow down their existing workflows or require code changes.
Solution
Red Hat deployed Tekton Chains, a Tekton component that automatically generates, signs, and stores supply chain attestations for every TaskRun and PipelineRun. Tekton Chains works by observing completed TaskRuns, extracting the inputs (source code commit, dependencies) and outputs (container image digests, built artifacts), and generating an in-toto attestation in SLSA provenance format. The attestation is signed using a Sigstore cosign key pair stored in a KMS (AWS KMS in their case), making the signatures non-forgeable without access to the KMS key. The signed attestation is then stored alongside the container image in the OCI registry as a cosign-compatible artifact. For SLSA Level 3 compliance, they configured Tekton to run builds on dedicated node pools with gVisor (runsc) container runtime for sandbox isolation, preventing builds from accessing the host filesystem or network beyond what was explicitly permitted. Build steps used hermetic builds: the git-clone task fetched source code, then the build steps ran with network access disabled (using a Kubernetes NetworkPolicy), ensuring that all dependencies came from the vendored or pre-fetched dependency cache rather than being pulled at build time from potentially compromised registries. They configured Tekton Chains to generate SLSA v1.0 provenance predicates that included the builder identity, source repository, source digest, build configuration, and all resolved dependencies with their checksums. Verification was automated using Kyverno policies on their OpenShift clusters: a ClusterPolicy required that any image deployed to production must have a valid Tekton Chains attestation signed by the trusted KMS key and the provenance must show that the build was hermetic.
Commands
kubectl apply -f https://storage.googleapis.com/tekton-releases/chains/latest/release.yaml # Install Tekton Chains
kubectl patch configmap chains-config -n tekton-chains -p '{"data":{"artifacts.taskrun.format":"slsa/v1","artifacts.taskrun.storage":"oci"}}' # Configure SLSA provenance formatcosign verify-attestation --type slsaprovenance --key cosign.pub registry.redhat.io/ubi9/httpd:latest # Verify SLSA provenance on image
tkn tr describe build-ubi9-run-x8k2j -o json | jq '.status.provenance' # Inspect provenance from TaskRun status
kubectl apply -f kyverno-policy-require-provenance.yaml # Enforce provenance verification at deploy time
Outcome
Achieved SLSA Level 3 compliance for all 450 container images within 72 days. Every container image now has cryptographically signed provenance linking it to a specific Git commit and build configuration. Build tampering detection became automatic: any image built outside the trusted Tekton pipeline is rejected at deployment time by Kyverno policies. Customer audit requests that previously took 2 weeks to fulfill are now self-service via cosign verify-attestation.
Lessons Learned
Supply chain security must be built into the CI/CD platform, not bolted on as a post-build step, because post-build attestation cannot prove what happened during the build. Hermetic builds (no network access during build steps) are essential for SLSA Level 3 but require pre-fetching all dependencies, which changes dependency management workflows. Tekton Chains' automatic attestation generation is the key to adoption because it requires zero changes to existing pipeline definitions.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────┐\n│ Tekton Pipeline │\n│ ┌──────────┐ ┌───────────┐ ┌──────────┐ │\n│ │git-clone │→ │ build │→ │ push │ │\n│ │(fetch src│ │ (hermetic │ │ (to OCI │ │\n│ │ + deps) │ │ no net) │ │ registry)│ │\n│ └──────────┘ └───────────┘ └─────┬────┘ │\n└─────────────────────────────────────┼────────────────┘\n │\n ┌───────────────────────┘\n ▼\n┌──────────────────────────────────────────────────────┐\n│ Tekton Chains (automatic) │\n│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │\n│ │Observe │→ │Generate SLSA │→ │Sign with │ │\n│ │TaskRun │ │Provenance │ │KMS (cosign)│ │\n│ │completion │ │attestation │ │ │ │\n│ └──────────────┘ └──────────────┘ └─────┬──────┘ │\n└────────────────────────────────────────────┼────────┘\n │\n ┌──────────────────────────────┘\n ▼\n┌──────────────────────────────────────────────────────┐\n│ OCI Registry │\n│ ┌─────────────────┐ ┌──────────────────────────┐ │\n│ │ Container Image │ │ Signed SLSA Attestation │ │\n│ │ sha256:abc123 │ │ (cosign artifact) │ │\n│ └─────────────────┘ └──────────────────────────┘ │\n└──────────────────────────────────────────────────────┘\n │\n ▼\n┌──────────────────────────────────────────────────────┐\n│ OpenShift Cluster (Kyverno Policy) │\n│ ┌────────────────────────────────────────┐ │\n│ │ Verify attestation before allowing │ │\n│ │ image deployment ✓ or ✗ │ │\n│ └────────────────────────────────────────┘ │\n└──────────────────────────────────────────────────────┘
💬 Comments
Context
Zalando, Europe's largest online fashion platform, processes 500+ commits per hour across 1,800 repositories during peak development hours. Their existing webhook-to-build system dropped 15% of events during traffic spikes, causing builds to not trigger and requiring developers to manually re-trigger failed webhook deliveries.
Problem
Zalando's CI/CD trigger system was built on a custom webhook receiver that parsed GitHub events and created build jobs in their pipeline system. During peak development hours (10 AM-12 PM CET), the webhook receiver processed over 500 events per hour, but its single-threaded architecture and synchronous build creation meant that GitHub's webhook delivery timeout of 10 seconds was regularly exceeded. When the receiver did not respond within the timeout, GitHub marked the delivery as failed and retried it, creating duplicate builds that wasted compute resources. During major release preparation periods when commit rates doubled, the receiver dropped approximately 15% of webhook events entirely, meaning builds were never triggered. Developers had to check whether their push triggered a build and manually re-trigger it if not, creating frustration and distrust in the CI/CD system. The webhook receiver also lacked sophisticated event filtering: every push to every branch triggered a full pipeline run, even for branches that only needed linting or for documentation-only changes. This generated approximately 40% unnecessary builds that consumed cluster resources. The receiver had no admission control: during a DDoS-like event where a bot pushed to 200 branches simultaneously, the system created 200 concurrent PipelineRuns that overwhelmed the Kubernetes cluster scheduler, causing cascading failures for unrelated workloads sharing the same cluster.
Solution
Zalando replaced their custom webhook receiver with Tekton Triggers, which provides a production-grade event handling system designed for high-throughput CI/CD triggering. They deployed the Tekton EventListener as a Kubernetes Deployment with horizontal pod autoscaling (HPA) configured to scale from 3 to 20 replicas based on CPU utilization and custom metrics from the events-per-second rate. The EventListener was fronted by an Ingress with rate limiting set to 100 requests per second per source IP, preventing bot-driven event floods. They implemented a chain of custom Interceptors that processed each webhook event before it reached the TriggerBinding. The first interceptor validated the GitHub webhook signature using HMAC-SHA256. The second interceptor was a custom CEL (Common Expression Language) interceptor that classified the event: documentation-only changes (files matching docs/** or *.md) were routed to a lightweight lint-only pipeline, feature branch pushes to a standard build pipeline, and main branch pushes to a full build-test-deploy pipeline. The third interceptor was a custom Go-based interceptor that implemented concurrency control: it checked how many PipelineRuns were currently active for the same repository and queued excess events in a Redis-backed queue, processing them as capacity became available. The TriggerTemplate created PipelineRuns with resource quotas based on the repository's tier (critical services got guaranteed resources, experimental services used best-effort). They also implemented idempotency: the TriggerBinding extracted the Git commit SHA and used it as the PipelineRun name, so duplicate webhook deliveries from GitHub retries simply resulted in a conflict error rather than duplicate builds.
Commands
kubectl apply -f eventlistener.yaml # Deploy EventListener with HPA and interceptor chain
tkn eventlistener logs github-listener -n ci-system -f # Stream EventListener processing logs
kubectl get pipelineruns -l triggers.tekton.dev/eventlistener=github-listener --sort-by=.metadata.creationTimestamp # List triggered pipeline runs
kubectl apply -f cel-interceptor-config.yaml # Deploy CEL interceptor for event classification
curl -X POST -H 'X-GitHub-Event: push' -H 'X-Hub-Signature-256: sha256=...' -d @payload.json http://el-github-listener.ci-system.svc:8080 # Test trigger manually
Outcome
Webhook event processing success rate improved from 85% to 99.97%. Duplicate builds eliminated through commit-SHA-based idempotency. Unnecessary builds reduced by 38% through intelligent event classification. Cluster stability improved dramatically with concurrency control preventing resource exhaustion during burst events.
Lessons Learned
Webhook reliability is a CI/CD concern that most teams underestimate until it causes missed builds. Idempotency through deterministic PipelineRun naming from commit SHAs is a simple but powerful pattern that eliminates an entire class of duplicate build issues. CEL interceptors provide a declarative, low-code way to implement complex event routing that would otherwise require custom webhook handler code.
◈ Architecture Diagram
┌──────────────────────────────────────────────────┐\n│ GitHub Webhooks │\n│ push push push push push │\n└──────┬───────┬───────┬───────┬───────┬───────────┘\n └───────┴───────┼───────┴───────┘\n ▼\n┌──────────────────────────────────────────────────┐\n│ Ingress (rate limit: 100 req/s per IP) │\n└──────────────────────┬───────────────────────────┘\n ▼\n┌──────────────────────────────────────────────────┐\n│ EventListener (HPA: 3-20 replicas) │\n│ │\n│ Interceptor Chain: │\n│ ┌───────────┐ ┌───────────┐ ┌──────────────┐ │\n│ │1. HMAC │→ │2. CEL │→ │3. Concurrency│ │\n│ │ Verify │ │ Classify │ │ Control │ │\n│ └───────────┘ └─────┬─────┘ └──────┬───────┘ │\n└───────────────────────┼───────────────┼──────────┘\n ┌─────────┼─────────┐ │\n ▼ ▼ ▼ ▼\n ┌────────┐ ┌────────┐ ┌──────────────┐\n │ Lint │ │ Build │ │ Build+Test │\n │Pipeline│ │Pipeline│ │+Deploy Pipel.│\n │(docs) │ │(branch)│ │(main branch) │\n └────────┘ └────────┘ └──────────────┘
💬 Comments
Context
Toyota Connected builds connected vehicle platforms processing data from 8 million vehicles. Automotive safety regulations (ISO 26262, UNECE WP.29) require auditable build records for all software deployed to vehicle systems. Their previous CI/CD system provided no immutable audit trail, risking regulatory non-compliance and potential vehicle recalls.
Problem
Toyota Connected's connected vehicle platform runs software on both cloud infrastructure and embedded vehicle systems. Automotive safety standards including ISO 26262 (functional safety) and UNECE WP.29 (cybersecurity regulations) mandate that every piece of software deployed to vehicle systems must have a complete, immutable, and auditable build record. This record must include the exact source code version, all dependencies with their versions, the build environment configuration, test results, security scan findings, and approval records. Their existing CI/CD system based on GitHub Actions provided build logs, but these logs were stored in GitHub's infrastructure with limited retention (90 days by default), could not be exported in a structured format for regulatory submission, and did not capture the level of detail required by auditors. During a regulatory audit in 2024, the compliance team spent three weeks manually reconstructing build records from GitHub Actions logs, Artifactory metadata, and Jira tickets. The auditor flagged several builds where the log retention had expired, meaning the build record was permanently lost. This audit finding triggered an urgent project to implement compliant CI/CD with full audit trails. Beyond compliance, the lack of structured pipeline metadata made it impossible to answer operational questions like 'What is the average pipeline duration trend over the last quarter?' or 'Which test stage fails most frequently across all services?'
Solution
Toyota Connected migrated to Tekton with Tekton Results as the pipeline metadata backbone. Tekton Results is a Tekton component that captures detailed, structured records of every TaskRun and PipelineRun, storing them in a PostgreSQL database with configurable retention policies. Unlike ephemeral pod logs, Results captures the full lifecycle of a pipeline run including parameters, workspace bindings, step statuses, step log references, and timing information as structured JSON. They configured Results to store records for 10 years to meet automotive regulatory retention requirements, with log content stored in an S3-compatible object store (MinIO) and metadata in PostgreSQL. For compliance-specific requirements, they created custom Tekton Tasks that integrated with their governance workflow. A gate-approval custom task paused the pipeline and created a Jira ticket requiring sign-off from the functional safety team. The pipeline only resumed when the Jira ticket was marked as approved, with the approval timestamp and approver identity recorded as a Result annotation. They created a security-scan task that ran both Trivy (container vulnerability scanning) and Semgrep (SAST), storing the scan results as Tekton Results annotations in a structured format that could be directly included in regulatory submissions. A SBOM-generation task used Syft to generate Software Bills of Materials in SPDX format, attached to the container image as a cosign artifact and recorded in Results. They built a compliance dashboard using Tekton Results' gRPC API that generated audit reports on demand, showing the complete chain of evidence from Git commit through build, test, scan, approval, and deployment for any artifact. The dashboard also provided real-time pipeline analytics for the engineering team.
Commands
kubectl apply -f https://storage.googleapis.com/tekton-releases/results/latest/release.yaml # Install Tekton Results
tkn results list --pipeline automotive-build --limit 50 # List recent pipeline results
tkn results records get pipelines/automotive-build/runs/abc123 -o json | jq '.annotations.security_scan' # Get security scan results from record
grpcurl -d '{"parent":"default/results/-","filter":"data.status.conditions[0].reason==\"Failed\""}' results-api:50051 tekton.results.v1alpha2.Results/ListResults # Query failed pipelines via gRPCkubectl apply -f sbom-generation-task.yaml # Deploy SBOM generation custom task
Outcome
Regulatory audit preparation time reduced from 3 weeks of manual work to 15 minutes of automated report generation. 100% of builds now have complete, immutable audit records with 10-year retention. The 2025 audit was completed in 2 days with zero findings, compared to 3 weeks with 4 findings in the previous audit. Pipeline analytics enabled a 31% improvement in overall pipeline duration by identifying consistently slow stages.
Lessons Learned
CI/CD audit compliance is not just about logging - it requires structured, queryable metadata that can be programmatically assembled into regulatory submissions. Tekton Results' separation of metadata (PostgreSQL) and log content (object storage) is architecturally sound because metadata queries are frequent but small, while log retrieval is infrequent but large. Custom tasks for approval gates transform the pipeline from a technical tool into a governance instrument that encodes organizational approval workflows.
◈ Architecture Diagram
┌──────────────────────────────────────────────────────┐\n│ Tekton Compliance Pipeline │\n│ │\n│ ┌──────┐ ┌───────┐ ┌──────┐ ┌────────────────┐ │\n│ │Clone │→ │ Build │→ │ Test │→ │ Security Scan │ │\n│ │ │ │ │ │ │ │ (Trivy+Semgrep)│ │\n│ └──────┘ └───────┘ └──────┘ └───────┬────────┘ │\n│ ▼ │\n│ ┌────────────────┐ ┌──────────┐ ┌──────────────┐ │\n│ │ SBOM Generate │← │Approval │← │ Gate (Jira │ │\n│ │ (Syft/SPDX) │ │ Recorded │ │ sign-off) │ │\n│ └───────┬────────┘ └──────────┘ └──────────────┘ │\n│ ▼ │\n│ ┌──────────────┐ │\n│ │ Deploy │ │\n│ └──────────────┘ │\n└──────────┬───────────────────────────────────────────┘\n │ All metadata captured automatically\n ▼\n┌──────────────────────────────────────────────────────┐\n│ Tekton Results │\n│ ┌──────────────┐ ┌───────────────────┐ │\n│ │ PostgreSQL │ │ MinIO (S3) │ │\n│ │ (metadata, │ │ (logs, SBOMs, │ │\n│ │ annotations,│ │ scan reports) │ │\n│ │ 10yr retain)│ │ │ │\n│ └──────┬───────┘ └───────────────────┘ │\n│ ▼ │\n│ ┌──────────────────────────┐ │\n│ │ Compliance Dashboard │ │\n│ │ (audit report generation)│ │\n│ └──────────────────────────┘ │\n└──────────────────────────────────────────────────────┘
💬 Comments
Context
Delivery Hero operates food delivery services in 70+ countries with 1,200 microservices. Their platform team of 8 engineers was spending 70% of their time handling CI/CD support tickets from 600+ developers, creating pipeline configurations, and debugging build failures that developers could not self-diagnose due to the complexity of their custom pipeline tooling.
Problem
Delivery Hero's platform engineering team had built a sophisticated CI/CD system using a combination of ArgoCD Workflows, custom bash scripts, and Helm charts. While powerful, the system had become so complex that only the 8 platform engineers understood how it worked. Developers who wanted to onboard a new microservice needed to file a ticket requesting pipeline creation, wait for a platform engineer to create the ArgoCD Workflow template and Helm values file, then iterate through multiple rounds of debugging when the pipeline did not work correctly for their specific technology stack. The average time from ticket creation to a working pipeline was 5.3 days. When builds failed, the error messages from the custom scripts were cryptic and undocumented, forcing developers to escalate to the platform team for debugging. The platform team was spending 70% of their time on reactive support rather than building new capabilities. The situation was unsustainable: as Delivery Hero expanded into new markets, the rate of new microservice creation was accelerating, but the platform team's capacity was fixed. Additionally, there was no standardization: each platform engineer had their own style for creating pipeline configurations, leading to inconsistency across the 1,200 services. Some pipelines ran security scans, others did not. Some deployed to staging automatically, others required manual triggers. There was no central catalog of what pipelines existed and what capabilities they included.
Solution
Delivery Hero rebuilt their CI/CD platform on Tekton with a self-service developer portal powered by Backstage. They created a Backstage Software Template that generated a complete Tekton pipeline configuration based on a developer-friendly form. The template asked simple questions: programming language, deployment target (Kubernetes namespace, cloud function, or static site), required quality gates (unit tests, integration tests, security scan, performance test), and deployment strategy (rolling update, blue-green, canary). Based on these inputs, the template generated Tekton Pipeline and Task YAML files, a .tekton/ directory in the repository, and registered the service in the Backstage catalog. The generated pipelines composed reusable Tekton Tasks from their internal TektonHub instance, which hosted 45 curated tasks covering builds for 12 programming languages, 8 security scanning tools, 5 deployment strategies, and various notification integrations. Each task was documented with clear input/output contracts and example usage. They built a Backstage plugin for Tekton that displayed PipelineRun status, logs, and history directly in the Backstage service catalog page, so developers never needed to use kubectl or the tkn CLI. The plugin included a 'Retry' button for failed runs and a 'Promote' button that triggered the deployment pipeline for the next environment. For pipeline debugging, each Task included structured error output using Tekton Results: when a build failed, the Result included the error category (compilation error, test failure, dependency resolution failure), the relevant log excerpt, and a link to the runbook for that error type. This reduced debugging escalations by enabling developers to self-diagnose most failures.
Commands
backstage-cli create-plugin --name tekton-pipelines # Create Backstage plugin for Tekton integration
tkn hub upload task golang-build --version 3.1.0 --catalog internal # Publish task to internal TektonHub
kubectl apply -f backstage-trigger-rbac.yaml # Configure RBAC for Backstage to create PipelineRuns
tkn pipeline list -n delivery-svc --output jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.labels.backstage\.io/template}{"\n"}{end}' # List pipelines with their Backstage template origincurl -s http://backstage.internal/api/tekton/pipelines/delivery-svc | jq '.runs[-1].status' # Query latest pipeline status via Backstage API
Outcome
New service onboarding time dropped from 5.3 days to 25 minutes (self-service via Backstage). Platform team support ticket volume reduced by 82%, freeing them to build new capabilities. Pipeline standardization achieved: 100% of services now have security scanning, up from 43%. Developer satisfaction with CI/CD (measured via internal survey) improved from 3.1/10 to 8.7/10.
Lessons Learned
Self-service is not about giving developers raw Tekton YAML to write; it is about providing abstractions that map to their mental model (language, deployment target, quality gates) and generating the correct technical configuration. A curated task catalog with clear documentation and versioning is more valuable than an open marketplace because it provides guardrails and consistency. Displaying CI/CD status within the developer portal (Backstage) rather than requiring kubectl access is essential for adoption by developers who are not Kubernetes-savvy.
◈ Architecture Diagram
┌──────────────────────────────────────────────────┐\n│ Developer Experience │\n│ │\n│ ┌──────────────────────────────────────────────┐│\n│ │ Backstage Portal ││\n│ │ ┌──────────────┐ ┌──────────────────────┐ ││\n│ │ │ Software │ │ Tekton Plugin │ ││\n│ │ │ Template │ │ (status, logs, │ ││\n│ │ │ (create svc)│ │ retry, promote) │ ││\n│ │ └──────┬───────┘ └──────────┬───────────┘ ││\n│ └─────────┼────────────────────┼──────────────┘│\n└────────────┼────────────────────┼───────────────┘\n │ │\n ▼ ▼\n┌──────────────────────────────────────────────────┐\n│ Kubernetes Cluster │\n│ │\n│ ┌──────────────────┐ ┌──────────────────────┐ │\n│ │ Generated │ │ Internal TektonHub │ │\n│ │ Pipeline YAML │ │ (45 curated tasks) │ │\n│ │ (.tekton/) │←─│ golang-build v3.1 │ │\n│ └────────┬─────────┘ │ trivy-scan v2.0 │ │\n│ │ │ helm-deploy v4.2 │ │\n│ ▼ └──────────────────────┘ │\n│ ┌──────────────────────────────────────────┐ │\n│ │ PipelineRun (self-service) │ │\n│ │ Build → Test → Scan → Deploy │ │\n│ │ │ │ │\n│ │ ▼ (on failure) │ │\n│ │ Structured error + runbook link │ │\n│ └──────────────────────────────────────────┘ │\n└──────────────────────────────────────────────────┘
💬 Comments