Tekton, the Kubernetes-native CI/CD framework, gets a lot of buzz, but its core components—Tasks, Pipelines, and Runs—are deceptively simple and elegantly orchestrated.

Let’s see a basic example in action. Imagine you want to build a Docker image and push it to a registry.

Here’s a Task that does just that:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-and-push
spec:
  params:
    - name: IMAGE_URL
      description: URL of the image to build and push (e.g., docker.io/myuser/myimage:latest)
  steps:
    - name: build
      image: gcr.io/cloud-builders/docker
      script: |
        docker build -t $(params.IMAGE_URL) .
    - name: push
      image: gcr.io/cloud-builders/docker
      script: |
        echo "$$(DOCKER_PASSWORD)" | docker login -u "$$(DOCKER_USERNAME)" --password-stdin
        docker push $(params.IMAGE_URL)
      env:
        - name: DOCKER_USERNAME
          valueFrom:
            secretKeyRef:
              name: docker-credentials
              key: username
        - name: DOCKER_PASSWORD
          valueFrom:
            secretKeyRef:
              name: docker-credentials
              key: password

This Task defines a sequence of steps. Each step is essentially a container that runs a command. The build-and-push task takes an IMAGE_URL parameter and uses the gcr.io/cloud-builders/docker image to first build the Docker image from the current directory (.) and then push it. Notice how it securely pulls Docker credentials from a Kubernetes Secret named docker-credentials.

Now, a Task is a reusable unit of work, but it doesn’t do anything on its own. To run a Task, you create a TaskRun.

Here’s a TaskRun that executes our build-and-push task:

apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
  name: build-and-push-run-1
spec:
  taskRef:
    name: build-and-push
  params:
    - name: IMAGE_URL
      value: "docker.io/myuser/myimage:v1.0.0"
  # To run this, you'd typically have a workspace defined
  # for the source code, e.g.:
  # workspaces:
  #   - name: source-code
  #     persistentVolumeClaim:
  #       claimName: my-source-pvc

When this TaskRun is created, Tekton provisions the necessary Kubernetes resources (like Pods) to execute the steps defined in the build-and-push Task, passing in the specified IMAGE_URL. The taskRef.name points to the Task we want to run. If your task needed access to source code, you’d define a workspace to provide that.

A Pipeline is where things get interesting. It’s a collection of Tasks orchestrated in a directed acyclic graph (DAG). This allows you to define multi-step workflows.

Let’s define a Pipeline that first clones a Git repository, then builds and pushes the Docker image:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: build-push-pipeline
spec:
  params:
    - name: IMAGE_URL
      description: URL of the image to build and push
    - name: GIT_REPO_URL
      description: URL of the Git repository
  tasks:
    - name: clone-repo
      taskRef:
        name: git-clone # Assuming a pre-defined git-clone Task
      params:
        - name: url
          value: $(params.GIT_REPO_URL)
      # A workspace is needed to output the cloned code
      workspaces:
        - name: output
          workspace: source-code-workspace # A PipelineWorkspace defined below

    - name: build-and-push-image
      taskRef:
        name: build-and-push
      runAfter: # This task runs only after clone-repo succeeds
        - clone-repo
      params:
        - name: IMAGE_URL
          value: $(params.IMAGE_URL)
      workspaces:
        - name: source-code # This maps to the workspace expected by build-and-push Task
          workspace: source-code-workspace # The same PipelineWorkspace

  workspaces: # Defines the shared workspaces for the pipeline
    - name: source-code-workspace

Here, the build-push-pipeline takes IMAGE_URL and GIT_REPO_URL as parameters. It has two tasks: clone-repo (which we assume is a pre-existing Tekton Task for cloning Git repos) and our build-and-push Task. The runAfter: - clone-repo directive ensures that the build-and-push-image task only executes after clone-repo has completed successfully. Notice how the source-code-workspace is defined at the Pipeline level and then mapped to the individual Tasksworkspaces. This is how data (like cloned source code) is shared between tasks in a Pipeline.

To run a Pipeline, you create a PipelineRun.

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: build-push-pipeline-run-1
spec:
  pipelineRef:
    name: build-push-pipeline
  params:
    - name: IMAGE_URL
      value: "docker.io/myuser/myimage:v1.0.1"
    - name: GIT_REPO_URL
      value: "https://github.com/myuser/myrepo.git"
  workspaces:
    - name: source-code-workspace
      persistentVolumeClaim:
        claimName: my-source-pvc # A PVC to store the cloned code

This PipelineRun triggers the execution of the build-push-pipeline. Tekton will manage the scheduling and execution of the clone-repo and build-and-push-image tasks in the correct order, passing parameters and sharing workspaces as defined. The workspaces section here links the Pipeline’s declared source-code-workspace to a specific Kubernetes PersistentVolumeClaim (PVC) that will be used for storage.

The most surprising thing about Tekton’s Task and Pipeline definitions is that they are purely declarative blueprints; they don’t contain any execution logic themselves. The actual work happens when a TaskRun or PipelineRun is created, which instructs the Tekton Controller to spin up Kubernetes Pods to execute the steps defined in the referenced Task or Pipeline. This separation of definition from execution is key to Tekton’s flexibility and Kubernetes-native nature.

The next logical step is to explore how to handle more complex dependencies between tasks, including conditional execution and parallel branches within a Pipeline.

Want structured learning?

Take the full Tekton course →