Blog
January 20, 2021 Marie H.

Cloud-Native CI/CD with Tekton Pipelines

Cloud-Native CI/CD with Tekton Pipelines

Photo by <a href="https://unsplash.com/@joshua_hoehne?utm_source=cloudista&utm_medium=referral" target="_blank" rel="noopener">Joshua Hoehne</a> on <a href="https://unsplash.com/?utm_source=cloudista&utm_medium=referral" target="_blank" rel="noopener">Unsplash</a>

Cloud-Native CI/CD with Tekton Pipelines

I've been running Jenkins for years. It works. It's also a sprawling Java process that needs its own ops story, its own plugins ecosystem, and its own babysitting. When you're already managing a Kubernetes cluster, standing up a separate CI server starts to feel like a contradiction. Tekton fixes that.

What Tekton Actually Is

Tekton is a Kubernetes-native CI/CD framework. There is no central server. There is no persistent process you have to keep alive. Your pipeline definitions are Kubernetes custom resources, your pipeline runs are Kubernetes pods, and your pipeline state lives in etcd alongside everything else in your cluster. This is either immediately appealing or immediately alarming depending on your relationship with Kubernetes.

The appeal: pipelines are portable. Any cluster with Tekton installed can run them. You version them in Git. You apply them with kubectl. They compose like everything else in k8s.

The alarm: the YAML surface area is significant and the abstractions take time to internalize.

Updated March 2026: Tekton has matured considerably since early 2021. The ecosystem is more stable and tooling like the Tekton CLI (tkn) has improved significantly. That said, the learning curve observation still stands.

Core Concepts

Task — the basic unit of work. A Task defines a sequence of Steps, each of which runs in a container. Tasks can declare inputs and outputs via Params and Results.

TaskRun — an instantiation of a Task. When you want to actually execute a Task, you create a TaskRun (or Tekton creates one for you as part of a pipeline).

Pipeline — a directed acyclic graph of Tasks. A Pipeline wires Tasks together, passing outputs from one as inputs to another.

PipelineRun — an instantiation of a Pipeline. This is what you actually trigger.

PipelineResource — the original way to express inputs/outputs like Git repos and container images. I'll show it here because it's what's in use right now, but read the updated note below.

Updated March 2026: PipelineResources were officially removed. The replacement is a combination of Workspaces (for shared storage between Tasks) and Results (for passing string outputs between Tasks). If you're starting fresh today, skip PipelineResources entirely and go straight to Workspaces + Results. The concepts in this post still translate directly — the plumbing just changed.

A Complete Go Build Pipeline

Here's a real pipeline for a Go service: lint, test, build image, push to registry.

First, the Tasks. I pull reusable ones from Tekton Hub where possible.

# Install from Tekton Hub
tkn hub install task golangci-lint
tkn hub install task golang-test
tkn hub install task buildah

Now a custom Task for anything not covered:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: go-build
spec:
  params:
    - name: package
      type: string
      description: The Go package path (e.g. github.com/myorg/myservice)
  workspaces:
    - name: source
  steps:
    - name: build
      image: golang:1.16
      workingDir: $(workspaces.source.path)
      env:
        - name: GOPATH
          value: /workspace/go
        - name: CGO_ENABLED
          value: "0"
      command: [go, build, -o, /dev/null, ./...]

Now the Pipeline that wires it all together:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: go-service-pipeline
spec:
  params:
    - name: repo-url
      type: string
    - name: image-name
      type: string
    - name: package-path
      type: string
  workspaces:
    - name: shared-workspace
    - name: docker-credentials
  tasks:
    - name: fetch-source
      taskRef:
        name: git-clone
      workspaces:
        - name: output
          workspace: shared-workspace
      params:
        - name: url
          value: $(params.repo-url)

    - name: lint
      runAfter: [fetch-source]
      taskRef:
        name: golangci-lint
      workspaces:
        - name: source
          workspace: shared-workspace
      params:
        - name: package
          value: $(params.package-path)

    - name: test
      runAfter: [fetch-source]
      taskRef:
        name: golang-test
      workspaces:
        - name: source
          workspace: shared-workspace
      params:
        - name: package
          value: ./...

    - name: build-and-push
      runAfter: [lint, test]
      taskRef:
        name: buildah
      workspaces:
        - name: source
          workspace: shared-workspace
        - name: dockerconfig
          workspace: docker-credentials
      params:
        - name: IMAGE
          value: $(params.image-name)

Notice lint and test both runAfter: [fetch-source] — they run in parallel. build-and-push waits for both. Tekton handles the DAG execution automatically.

To trigger it:

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  generateName: go-service-run-
spec:
  pipelineRef:
    name: go-service-pipeline
  params:
    - name: repo-url
      value: https://github.com/myorg/myservice
    - name: image-name
      value: registry.myorg.com/myservice:latest
    - name: package-path
      value: github.com/myorg/myservice
  workspaces:
    - name: shared-workspace
      volumeClaimTemplate:
        spec:
          accessModes: [ReadWriteOnce]
          resources:
            requests:
              storage: 1Gi
    - name: docker-credentials
      secret:
        secretName: registry-credentials

Tekton Hub

Tekton Hub (hub.tekton.dev) is a catalog of reusable Tasks. Git clone, Buildah, Kaniko, Helm deploy, kubectl apply — most of what you need is already there. I treat it the way I treat Docker Hub: start there, fork and customize when the upstream task doesn't quite fit.

How It Compares

vs GitHub Actions: Actions is easier to get started with. The marketplace is massive. But your pipelines are tied to GitHub, and the runner model is fundamentally different — you're not getting the same Kubernetes-native scheduling and resource isolation. For teams not all-in on k8s, Actions wins on simplicity. For teams that are, Tekton's portability and GitOps-friendliness are hard to give up.

vs Jenkins: Jenkins has every plugin imaginable and a 15-year head start. It also has the ops burden of running Jenkins itself, managing agents, dealing with plugin compatibility hell. Tekton is more declarative, more composable, and scales better on k8s. The tradeoff is real: Jenkins is more approachable for developers who just want to write a Jenkinsfile and move on. Tekton rewards investment.

My Honest Take

Tekton is not the path of least resistance. The first pipeline you write will take longer than you expect. The abstractions are clean once you internalize them, but getting there requires patience. I use it because I'm already deep in Kubernetes and I want my CI/CD to live in the same operational model as everything else — same GitOps workflow, same RBAC, same observability tooling. If you're not already in that world, there are easier options. If you are, Tekton is worth the investment.