Tekton Workspaces let you share data between Tasks, but they’re not just about passing files; they’re about managing mutable state across distributed, ephemeral environments.

Let’s see this in action. Imagine a build-and-deploy pipeline. The build Task needs to produce artifacts (like a compiled binary or a Docker image). The deploy Task needs those artifacts to do its job.

Here’s a simplified Pipeline:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: build-and-deploy
spec:
  workspaces:
    - name: shared-data
  tasks:
    - name: build-app
      taskRef:
        name: build-task # This task will produce artifacts
      workspaces:
        - name: output
          workspace: shared-data
    - name: deploy-app
      taskRef:
        name: deploy-task # This task will consume artifacts
      runAfter:
        - build-app
      workspaces:
        - name: input
          workspace: shared-data

And the Tasks:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-task
spec:
  workspaces:
    - name: output # This is where build-task writes its output
  steps:
    - name: build
      image: ubuntu:latest
      script: |
        #!/bin/bash
        echo "Building the app..."
        mkdir -p $(workspaces.output.path)/artifacts
        echo "Hello from the build!" > $(workspaces.output.path)/artifacts/hello.txt
        echo "Build complete."
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: deploy-task
spec:
  workspaces:
    - name: input # This is where deploy-task reads its input
  steps:
    - name: deploy
      image: ubuntu:latest
      script: |
        #!/bin/bash
        echo "Deploying the app..."
        if [ -f "$(workspaces.input.path)/artifacts/hello.txt" ]; then
          echo "Found artifact: $(cat $(workspaces.input.path)/artifacts/hello.txt)"
        else
          echo "Artifact not found!"
          exit 1
        fi
        echo "Deployment complete."

When you run a PipelineRun for build-and-deploy, Tekton provisions a volume, mounts it into the build-task at the path specified by workspaces.output.path, and then mounts the same volume into the deploy-task at the path specified by workspaces.input.path. The build-task writes hello.txt into that volume, and the deploy-task reads it.

The core problem Workspaces solve is ephemeral execution environments. Each Task runs in its own Pod, and by default, when a Pod dies, its filesystem is gone. Workspaces provide a persistent volume that outlives individual Pods within a PipelineRun. This allows for a staged process: one Task produces, another consumes.

You control the lifecycle and type of these shared volumes through the Pipeline’s workspaces definition. The name in the Pipeline’s workspaces list is the logical name used within the Pipeline. When you create a PipelineRun, you bind this logical name to a concrete PersistentVolumeClaim (PVC) or a volumeClaimTemplate (which Tekton creates a PVC from).

If you don’t specify a PVC or volumeClaimTemplate in the PipelineRun for a Workspace that a Task requires, Tekton defaults to using an emptyDir volume. emptyDir volumes are tied to the Pod’s lifecycle and are wiped when the Pod terminates. This is useful for sharing data within a single Task’s steps, but not between different Tasks in a Pipeline.

To ensure data persists between Tasks, you must bind your Pipeline Workspace to a persistent storage mechanism. A common pattern is using a volumeClaimTemplate directly in the Pipeline definition. This tells Tekton to dynamically provision a PVC when the PipelineRun starts, using the specified storage class and access modes.

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: build-and-deploy-with-pvc
spec:
  workspaces:
    - name: shared-data
      volumeClaimTemplate: # Tekton will create a PVC based on this
        metadata:
          name: pipeline-shared-data-pvc # Name of the PVC to be created
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 1Gi
          storageClassName: standard # Or your specific storage class
  tasks:
    # ... (tasks as before, referencing 'shared-data' workspace)

When a PipelineRun using this Pipeline starts, Tekton will create a PVC named pipeline-shared-data-pvc (or a unique name if multiple PipelineRuns run concurrently and the template name clashes) and mount it to all Tasks that reference the shared-data Workspace.

The volumeClaimTemplate is key here. It allows you to define the characteristics of the persistent storage (size, access mode, storage class) that Tekton will request from your Kubernetes cluster. If you don’t specify volumeClaimTemplate, and you don’t bind a pre-existing PVC in your PipelineRun, you’ll get an emptyDir.

Consider the accessModes. ReadWriteOnce (RWO) is the most common and means the volume can be mounted as read-write by a single node. If you have multiple Tasks that might run on different nodes simultaneously and need to write, you’d need ReadWriteMany (RWX), which requires a storage provisioner that supports it (like NFS, CephFS, or cloud provider block storage with specific configurations). However, Tekton’s default behavior for a single PipelineRun typically ensures Tasks are scheduled such that RWO is sufficient because the volume is mounted into pods that are scheduled onto nodes, and Kubernetes handles the underlying node affinity for the volume. The main constraint is the storage provider’s capability.

You can also bind an existing PVC by referencing it in the PipelineRun’s workspaces section. This is useful if you want to reuse a specific volume or have pre-populated data.

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: build-and-deploy-run
spec:
  pipelineRef:
    name: build-and-deploy # Assuming this pipeline uses 'shared-data' workspace
  workspaces:
    - name: shared-data # Matches the workspace name in the Pipeline
      persistentVolumeClaim:
        claimName: my-existing-shared-volume-pvc # Name of your pre-created PVC

The core insight is that Workspaces are not just for passing files; they’re a mechanism for defining and managing the lifecycle of the storage that connects your ephemeral Task Pods. The volumeClaimTemplate is the declarative way to provision this storage on-demand, ensuring your pipeline stages can reliably exchange data.

If your Tasks are writing large amounts of data to a Workspace and you’re seeing performance issues, the next thing you’ll likely investigate is the underlying storage class and its IOPS/throughput capabilities.

Want structured learning?

Take the full Tekton course →