Tekton’s embedded specs let you define Tasks directly within your Pipeline, making your CI/CD definitions more localized and easier to manage for simpler workflows.
Let’s see this in action. Imagine you have a Pipeline that needs to build and then push a Docker image. Instead of defining a separate Task resource for building and another for pushing, you can embed their specifications directly within the Pipeline resource itself.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: embedded-build-push
spec:
tasks:
- name: build-and-push-image
params:
- name: IMAGE_NAME
value: "my-docker-repo/my-app"
- name: IMAGE_TAG
value: "latest"
taskSpec:
params:
- name: IMAGE_NAME
type: string
- name: IMAGE_TAG
type: string
steps:
- name: build-image
image: docker:latest
script: |
#!/bin/bash
echo "Building image $(params.IMAGE_NAME):$(params.IMAGE_TAG)"
# Simulate building the image
docker build -t $(params.IMAGE_NAME):$(params.IMAGE_TAG) .
- name: push-image
image: docker:latest
script: |
#!/bin/bash
echo "Pushing image $(params.IMAGE_NAME):$(params.IMAGE_TAG)"
# Simulate pushing the image (requires docker login)
docker push $(params.IMAGE_NAME):$(params.IMAGE_TAG)
workspaces:
- name: source
description: Workspace containing source code
workspaces:
- name: source
description: Workspace for source code
finally:
- name: cleanup
taskSpec:
steps:
- name: remove-image
image: docker:latest
script: |
#!/bin/bash
echo "Cleaning up local image $(params.IMAGE_NAME):$(params.IMAGE_TAG)"
docker rmi $(params.IMAGE_NAME):$(params.IMAGE_TAG)
In this example, the build-and-push-image task has a taskSpec field. This taskSpec contains the full definition of a Task – its parameters, steps, and workspaces. The Pipeline then references this embedded taskSpec as if it were a standalone Task resource.
The primary problem embedded specs solve is reducing the cognitive load and management overhead for simpler, self-contained workflows. Instead of maintaining multiple Task YAML files that are only ever used by a single Pipeline, you can colocate their definitions. This is particularly useful when a Task is highly specific to one Pipeline and unlikely to be reused elsewhere. It also helps in understanding a Pipeline’s functionality at a glance, as all its constituent steps are visible within the Pipeline definition itself.
Internally, when Tekton processes this Pipeline, it effectively treats the taskSpec as if it were a separate Task resource. When a PipelineRun is created, Tekton resolves these embedded taskSpec definitions and creates the corresponding TaskRun resources accordingly. The params defined at the Pipeline level can be passed directly to the taskSpec, and parameters within the taskSpec can reference these passed-in values using the $(params.PARAM_NAME) syntax. The same applies to workspaces.
You can also embed multiple tasks within a single Pipeline, and even define tasks in the finally section using taskSpec. This allows for comprehensive, self-contained Pipeline definitions that bundle all necessary logic.
A subtle but powerful aspect is that the taskSpec can define parameters that are not provided by the Pipeline itself. These will need to be provided when the Pipeline is executed, either directly in the PipelineRun or via default values within the taskSpec. This offers a way to define "private" parameters for an embedded task that are not exposed at the Pipeline level, further encapsulating its behavior.
The next logical step after mastering embedded specs is exploring PipelineTemplates, which allow you to define reusable Pipeline structures that can be instantiated with different Task definitions, including embedded ones.