Tekton’s Pipeline resource can run multiple Tasks in parallel, controlled by parameters, to create dynamic and efficient CI/CD workflows.

Let’s see this in action. Imagine you have a set of microservices, each needing to be built and pushed to a registry. Instead of a single Pipeline with a hardcoded list of services, you want to parameterize this so you can build any subset, or all, of them at once.

Here’s a Pipeline that does just that:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: matrix-build-push
spec:
  params:
    - name: services
      description: A comma-separated list of services to build and push.
      type: string
      default: "service-a,service-b,service-c"
  tasks:
    - name: build-and-push-service
      taskRef:
        name: build-and-push # Assumes you have a Task named build-and-push
      params:
        - name: service-name
          value: $(tasks.build-and-push-service.params.service-name)
      matrix:
        # This is the magic: iterate over each service provided in the input param
        services: $(split(params.services, ','))
      # The 'when' condition ensures this task only runs if the matrix iteration is active
      when: $(tasks.build-and-push-service.matrix.services != '')

And here’s a simplified Task that build-and-push-service might reference:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-and-push
spec:
  params:
    - name: service-name
      description: The name of the service to build.
      type: string
  steps:
    - name: build-and-push
      image: gcr.io/cloud-builders/docker
      script: |
        echo "Building and pushing $(params.service-name)..."
        # In a real scenario, this would be actual docker build and push commands
        # e.g., docker build -t my-registry/$(params.service-name):latest .
        # e.g., docker push my-registry/$(params.service-name):latest
        sleep 10 # Simulate work
        echo "Finished $(params.service-name)."

When you trigger a PipelineRun for matrix-build-push, you can pass in the services parameter. For example, to build only service-a and service-c:

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: matrix-build-push-run-a-c
spec:
  pipelineRef:
    name: matrix-build-push
  params:
    - name: services
      value: "service-a,service-c"

Tekton will then instantiate the build-and-push-service task twice, once for service-a and once for service-c. The matrix field in the Pipeline definition tells Tekton to take the value of $(split(params.services, ',')) (which is an array of strings like ["service-a", "service-c"]) and create a separate instance of the task for each element in that array. Each instance gets its own unique value for $(tasks.build-and-push-service.matrix.services), which is then used to dynamically set the service-name parameter for that specific task instance. The when condition is crucial here; it ensures that the task logic only executes when the services matrix variable actually has a value assigned to it for that specific task instance. This is how Tekton allows you to generate multiple task runs from a single Pipeline definition based on input parameters.

The core problem this solves is avoiding repetitive Pipeline definitions for similar, but slightly different, workloads. Instead of having build-service-a, build-service-b, and build-service-c PipelineRuns, you have one Pipeline and dynamic PipelineRuns. This dramatically reduces YAML sprawl and makes your CI/CD system far more maintainable and adaptable. The matrix feature, combined with parameterization and the split function, lets you define a template that can generate a variable number of parallel task executions on the fly.

A common point of confusion is how the params within the matrix section of the task work. When you define value: $(tasks.build-and-push-service.params.service-name) inside the matrix block, you’re telling Tekton that for each iteration of the matrix, the service-name parameter for that specific instance of the build-and-push-service task should be set to the value of $(tasks.build-and-push-service.matrix.services). This is how each parallel task instance receives its unique input. The $(tasks.build-and-push-service.matrix.services) expression refers to the current element being processed by the matrix loop.

The next thing you’ll likely run into is needing to aggregate results or perform a subsequent step only after all parallel tasks have completed.

Want structured learning?

Take the full Tekton course →