Workspaces provide flexible storage for your Tasks and Pipelines. This guide covers advanced workspace patterns and techniques.
Overview
Advanced workspace features include:
Using subpaths to organize workspace data
Isolating workspace access to specific Steps
Optional workspaces with conditional logic
Sharing workspaces across Pipeline Tasks
Dynamic workspace configuration
Workspace Subpaths
Subpaths allow multiple Tasks to use different directories within the same workspace:
Basic Subpath Usage
apiVersion : tekton.dev/v1
kind : Pipeline
metadata :
name : pipeline-using-different-subpaths
spec :
workspaces :
- name : ws
tasks :
- name : writer-1
taskRef :
name : writer
workspaces :
- name : task-ws
workspace : ws
subPath : dir-1
- name : writer-2
runAfter :
- writer-1
taskRef :
name : writer
workspaces :
- name : task-ws
workspace : ws
subPath : dir-2
- name : read-all
runAfter :
- writer-2
taskRef :
name : read-both
workspaces :
- name : local-ws
workspace : ws
The Task definitions:
apiVersion : tekton.dev/v1
kind : Task
metadata :
name : writer
spec :
workspaces :
- name : task-ws
steps :
- name : write
image : ubuntu
script : echo bar > $(workspaces.task-ws.path)/foo
---
apiVersion : tekton.dev/v1
kind : Task
metadata :
name : read-both
spec :
params :
- name : directory1
- name : directory2
workspaces :
- name : local-ws
steps :
- name : read-1
image : ubuntu
script : cat $(workspaces.local-ws.path)/$(params.directory1)/foo | grep bar
- name : read-2
image : ubuntu
script : cat $(workspaces.local-ws.path)/$(params.directory2)/foo | grep bar
Running the Pipeline:
apiVersion : tekton.dev/v1
kind : PipelineRun
metadata :
generateName : pr-different-path-
spec :
pipelineRef :
name : pipeline-using-different-subpaths
workspaces :
- name : ws
volumeClaimTemplate :
spec :
accessModes :
- ReadWriteOnce
resources :
requests :
storage : 1Gi
Parameterized Subpaths
Use parameters to dynamically configure subpaths:
apiVersion : tekton.dev/v1
kind : Pipeline
metadata :
name : parameterized-subpath
spec :
params :
- name : environment
type : string
workspaces :
- name : shared-workspace
tasks :
- name : build
params :
- name : env
value : $(params.environment)
workspaces :
- name : source
workspace : shared-workspace
subPath : $(params.environment)/build
taskRef :
name : build-task
Use parameterized subpaths to organize builds by environment, branch, or other dynamic criteria.
Isolated Workspaces
Limit workspace access to specific Steps within a Task:
apiVersion : tekton.dev/v1
kind : PipelineRun
metadata :
generateName : isolated-workspaces-
spec :
workspaces :
- name : ssh-credentials
secret :
secretName : test-ssh-credentials
pipelineSpec :
workspaces :
- name : ssh-credentials
tasks :
- name : test-isolation
workspaces :
- name : creds
workspace : ssh-credentials
taskSpec :
workspaces :
- name : creds
steps :
# This step has access to credentials
- name : use-creds
image : alpine
script : |
if [ ! -d /creds ] ; then
echo "unable to access creds"
exit 1
fi
# Use credentials
ls /creds
workspaces :
- name : creds
mountPath : /creds
# This step cannot see credentials
- name : no-creds
image : alpine
script : |
if [ -d /workspace/creds ] ; then
echo "this step should not be able to see creds"
exit 255
fi
echo "Credentials properly isolated"
By default, workspaces are mounted to all Steps. Use the workspaces field in a Step to override the default mount path or isolate access.
Optional Workspaces
Make workspaces optional and handle their presence conditionally:
apiVersion : tekton.dev/v1
kind : Task
metadata :
name : run-js
spec :
workspaces :
- name : prelaunch
optional : true
description : Optional prelaunch script workspace
- name : launch
description : Required launch script workspace
steps :
- name : run
image : node:lts-alpine
script : |
#!/usr/bin/env sh
if [ "$(workspaces.prelaunch.bound)" == "true" ] ; then
echo "Running prelaunch script"
node "$(workspaces.prelaunch.path)/init.js"
else
echo "Skipping prelaunch - workspace not provided"
fi
if [ -f "$(workspaces.launch.path)/main.js" ] ; then
node "$(workspaces.launch.path)/main.js"
else
echo "Error: missing main.js file in launch workspace!"
exit 1
fi
Using the Task in a Pipeline:
apiVersion : tekton.dev/v1
kind : PipelineRun
metadata :
generateName : optional-workspaces-
spec :
workspaces :
- name : launch
configMap :
name : example-source-code-configmap
# prelaunch workspace intentionally omitted
pipelineSpec :
workspaces :
- name : launch
- name : prelaunch
optional : true
tasks :
- name : print-bound-state
taskRef :
name : echo
params :
- name : values
value :
- "Was a prelaunch workspace provided? "
- "$(workspaces.prelaunch.bound)"
- "Was a launch workspace provided? "
- "$(workspaces.launch.bound)"
- name : run-js
workspaces :
- name : prelaunch
workspace : prelaunch
- name : launch
workspace : launch
taskRef :
name : run-js
Workspace Bound Variable
Check if an optional workspace was provided:
script : |
if [ "$(workspaces.cache.bound)" == "true" ]; then
echo "Using cache from $(workspaces.cache.path)"
cp -r $(workspaces.cache.path)/* .
else
echo "No cache workspace provided"
fi
Workspace Propagation
Share a Pipeline workspace across multiple Tasks:
apiVersion : tekton.dev/v1
kind : Pipeline
metadata :
name : workspace-propagation
spec :
workspaces :
- name : shared-data
tasks :
- name : fetch-source
workspaces :
- name : output
workspace : shared-data
taskRef :
name : git-clone
- name : run-tests
runAfter :
- fetch-source
workspaces :
- name : source
workspace : shared-data
taskRef :
name : golang-test
- name : build
runAfter :
- run-tests
workspaces :
- name : source
workspace : shared-data
taskRef :
name : golang-build
Each Task can map the Pipeline workspace to a different workspace name within the Task.
Workspace Types
PersistentVolumeClaim
Use existing PVC:
workspaces :
- name : source
persistentVolumeClaim :
claimName : my-pvc
Create PVC dynamically:
workspaces :
- name : source
volumeClaimTemplate :
spec :
accessModes :
- ReadWriteOnce
resources :
requests :
storage : 1Gi
storageClassName : fast-ssd
ConfigMap
workspaces :
- name : config
configMap :
name : my-config
items :
- key : config.yaml
path : app-config.yaml
Secret
workspaces :
- name : credentials
secret :
secretName : my-secret
items :
- key : token
path : .token
EmptyDir
workspaces :
- name : temp
emptyDir : {}
CSI Volumes
workspaces :
- name : csi-workspace
csi :
driver : secrets-store.csi.k8s.io
readOnly : true
volumeAttributes :
secretProviderClass : my-secret-provider
Workspace Variables
Access workspace metadata in your Tasks:
steps :
- name : workspace-info
image : alpine
script : |
echo "Workspace path: $(workspaces.source.path)"
echo "Workspace bound: $(workspaces.source.bound)"
echo "PVC name: $(workspaces.source.claim)"
echo "Volume name: $(workspaces.source.volume)"
Variable Description workspaces.name.pathMount path of the workspace workspaces.name.boundWhether the workspace was provided workspaces.name.claimPVC name (empty for other types) workspaces.name.volumeVolume name
Advanced Patterns
Cache Workspace Pattern
apiVersion : tekton.dev/v1
kind : Pipeline
metadata :
name : build-with-cache
spec :
workspaces :
- name : cache
optional : true
tasks :
- name : build
workspaces :
- name : cache
workspace : cache
taskSpec :
workspaces :
- name : cache
optional : true
steps :
- name : restore-cache
image : node
script : |
if [ "$(workspaces.cache.bound)" == "true" ]; then
echo "Restoring from cache"
cp -r $(workspaces.cache.path)/node_modules . || true
fi
- name : build
image : node
script : |
npm install
npm run build
- name : save-cache
image : node
script : |
if [ "$(workspaces.cache.bound)" == "true" ]; then
echo "Saving to cache"
cp -r node_modules $(workspaces.cache.path)/
fi
Multi-Environment Pattern
apiVersion : tekton.dev/v1
kind : PipelineRun
metadata :
generateName : multi-env-
spec :
params :
- name : target-env
value : staging
workspaces :
- name : manifests
persistentVolumeClaim :
claimName : deployment-manifests
pipelineSpec :
params :
- name : target-env
workspaces :
- name : manifests
tasks :
- name : deploy
params :
- name : environment
value : $(params.target-env)
workspaces :
- name : configs
workspace : manifests
subPath : environments/$(params.target-env)
taskRef :
name : kubectl-apply
Temporary Workspace for Build Artifacts
tasks :
- name : build-and-test
workspaces :
- name : source
workspace : source-code
taskSpec :
workspaces :
- name : source
steps :
- name : build
image : golang
workingDir : $(workspaces.source.path)
script : |
go build -o /tmp/app ./cmd/app
- name : test
image : golang
script : |
/tmp/app --version
/tmp/app --test
Best Practices
Use VolumeClaimTemplates for Dynamic Storage
Use volumeClaimTemplate instead of creating PVCs manually. This allows Tekton to manage PVC lifecycle automatically.
workspaces :
- name : source
volumeClaimTemplate :
spec :
accessModes :
- ReadWriteOnce
resources :
requests :
storage : 1Gi
Organize with Subpaths
# Organize by purpose
subPath : source/$(params.repo-name)
subPath : artifacts/$(params.build-id)
subPath : cache/npm
subPath : logs/$(context.taskRun.name)
Clean Up After Use
finally :
- name : cleanup
workspaces :
- name : workspace
workspace : shared-workspace
taskSpec :
workspaces :
- name : workspace
steps :
- name : clean
image : alpine
script : |
rm -rf $(workspaces.workspace.path)/*
Use Optional Workspaces for Flexibility
workspaces :
- name : maven-cache
optional : true
description : Optional Maven cache to speed up builds
- name : docker-config
optional : true
description : Optional Docker config for private registries
Validate Workspace Contents
steps :
- name : validate
image : alpine
script : |
if [ ! -f "$(workspaces.source.path)/pom.xml" ]; then
echo "Error: pom.xml not found in workspace"
exit 1
fi
When should I use subpaths vs separate workspaces?
Use subpaths when:
Multiple Tasks need to share a single PVC
You want to organize data hierarchically
You need to reduce PVC costs
Tasks write to different directories
Use separate workspaces when:
Data has different lifecycle requirements
You need different access modes (ReadOnly vs ReadWrite)
Tasks require isolation for security
Different storage classes are needed
How do I debug workspace issues?
Common debugging steps:
Check workspace is bound :
script : |
echo "Bound: $(workspaces.name.bound)"
echo "Path: $(workspaces.name.path)"
List workspace contents :
script : |
ls -la $(workspaces.name.path)
Check permissions :
script : |
ls -ld $(workspaces.name.path)
id
Verify PVC :
kubectl get pvc
kubectl describe pvc < pvc-nam e >