Tekton’s Task steps are where the actual work happens, and they achieve this by running container images, executing commands within them, and optionally sharing data via volumes.

Let’s see a Task in action that builds a simple Go application.

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-go-app
spec:
  params:
    - name: GO_VERSION
      description: The version of Go to use
      type: string
      default: "1.19"
  steps:
    - name: checkout-source
      image: alpine/git:latest
      script: |
        git clone https://github.com/tektoncd/pipeline /workspace/source
    - name: build
      image: $(params.GO_VERSION)-alpine
      workingDir: /workspace/source/cmd/app
      script: |
        apk update && apk add make
        make build
      volumeMounts:
        - name: workspace
          mountPath: /workspace
    - name: upload-artifact
      image: alpine:latest
      script: |
        echo "Build successful!" > /workspace/build-status.txt
      volumeMounts:
        - name: workspace
          mountPath: /workspace
  volumes:
    - name: workspace
      emptyDir: {}

In this Task, we have three steps: checkout-source, build, and upload-artifact. Each step runs within a container image. The checkout-source step uses alpine/git:latest to clone a Git repository into /workspace/source.

The build step is where the compilation happens. It uses a Go image specified by the GO_VERSION parameter (e.g., 1.19-alpine). Notice the workingDir is set to /workspace/source/cmd/app, so commands are executed relative to that directory. We first install make using apk and then run make build. This step also mounts the workspace volume, which is crucial for sharing data between steps.

Finally, the upload-artifact step, running in a minimal alpine:latest image, writes a success message to /workspace/build-status.txt. This file will be accessible by subsequent steps or Tasks if this TaskRun is part of a Pipeline.

The volumes section defines a volume named workspace. The emptyDir: {} means it’s a temporary directory that exists for the life of the Pod running the Task. Any data written to this volume by one step is available to other steps.

The core problem Tekton Task steps solve is providing a standardized, containerized way to execute arbitrary build and deployment logic. Instead of relying on a single monolithic CI/CD runner image, you define discrete, reusable Tasks. Each Task is composed of steps, and each step is essentially a container that runs a specific command. This promotes modularity and reusability.

Internally, each step in a Task runs as a separate container within the same Kubernetes Pod. The workingDir specifies the directory inside the container where the script will be executed. The script field itself is a shell script that gets executed. If you need to run multiple commands, you can put them in the script, or use command and args to run a specific executable with its arguments.

The image field is where you specify the container image to use for the step. This can be a standard image like ubuntu, node, or golang, or a custom image you’ve built. The volumeMounts section is key to inter-step communication. When you define a volume in the Task spec and then mount it into a step, that step can read and write data to that volume, making it available to other steps that also mount the same volume. emptyDir is the most common volume type for Task steps, providing ephemeral storage for the Pod.

A common pattern is to use an emptyDir volume mounted at /workspace and have all your steps operate within subdirectories of /workspace. This ensures that files created or modified by one step (like compiled binaries or downloaded dependencies) are available to subsequent steps. The image can also be dynamic, using params to select different versions of tools or even entirely different images based on configuration.

What most people don’t realize is that the script field in a Tekton step is actually wrapped in a shell (usually /bin/sh -c) by default. This means you can use shell features like pipes, redirection, and environment variable expansion directly within your script block. If you need to execute a command without shell processing, you’d use the command and args fields instead.

The next concept you’ll likely encounter is how to combine these Tasks into more complex workflows using Pipelines.

Want structured learning?

Take the full Tekton course →