Hands-on Demonstration of Tekton Pipelines on OpenShift
I am here for giving wonderful knowledge to society
Introduction
Modern CI/CD requires pipelines that are cloud‑native, Kubernetes‑native, scalable, and vendor‑neutral. Tekton Pipelines, a CNCF project, fits perfectly into this space. Red Hat OpenShift provides first‑class support for Tekton through the OpenShift Pipelines Operator.
In this blog, we’ll walk through a step‑by‑step demonstration of a Tekton Pipeline on OpenShift, from installation to execution, using a simple build pipeline.
What is Tekton?
Tekton is a Kubernetes‑native framework for creating CI/CD pipelines. Everything in Tekton is defined as Kubernetes CRDs (Custom Resource Definitions).
Core Tekton Concepts
Task – A single unit of work (e.g., build, test, push image)
TaskRun – Execution of a Task
Pipeline – A collection of Tasks connected together
PipelineRun – Execution of a Pipeline
Workspace – Shared storage between tasks
Trigger – Event‑based pipeline execution (GitHub webhook, etc.)
Why Tekton on OpenShift?
Native integration with OpenShift
Built‑in web console visualization
Secure by default (SCC, RBAC)
Operator‑based lifecycle management
Works seamlessly with OpenShift Builds, ImageStreams, and Routes
Prerequisites
Before starting, ensure you have:
OpenShift cluster (4.x)
Cluster admin or sufficient privileges
ocCLI installed and configuredBasic understanding of Kubernetes concepts
Step 1: Install OpenShift Pipelines Operator
Log in to the OpenShift Web Console
Navigate to Operators → OperatorHub
Search for OpenShift Pipelines
Click Install (default settings are fine)
Verify installation:
oc get pods -n openshift-pipelines
1️⃣ Tekton Task – The Building Block
What is a Task?
A Task is the smallest unit of work in Tekton. Each task:
Runs inside its own Kubernetes Pod
Contains one or more steps
Uses container images to execute commands
Example Tasks in Our Demo
In our Node.js CI/CD setup, we use multiple tasks:
git-clone-task→ Clones source code from Gitnodejs-build-test→ Installs dependencies, runs tests, builds the appdeploy-to-openshift→ Deploys the app to OpenShift
Key Characteristics of Tasks
Tasks are reusable
Tasks are independent
Tasks do not share data by default
This last point is very important and leads us to workspaces.
2️⃣ Workspace – Sharing Data Between Tasks
Why Workspaces Are Needed
In Tekton:
Each task runs in a separate pod
Each pod has its own filesystem
But in CI/CD pipelines:
One task clones code
Another task builds the same code
Another task deploys it
A Workspace solves this problem by providing shared storage.
Simple Definition
A Workspace is a shared directory mounted into multiple task pods.
Workspace at Different Levels
Task Level
A task declares that it needs a workspace:
workspaces:
- name: source
Pipeline Level
The pipeline connects workspaces between tasks:
workspaces:
- name: shared-data
workspaces:
- name: source
workspace: shared-data
PipelineRun Level
The PipelineRun provides the actual storage:
workspaces:
- name: shared-data
emptyDir: {}
3️⃣ Tekton Pipeline – Orchestrating Tasks
What is a Pipeline?
A Pipeline defines:
Which tasks should run
In what order they should run
How data flows between them
A pipeline itself does not execute anything.
Example: Node.js CI/CD Pipeline Flow
Our pipeline performs the following steps:
Clone source code from Git
Build and test the Node.js application
Deploy the application to OpenShift
Pipeline Parameters
params:
- name: git-url
- name: git-revision
- name: app-name
Parameters make pipelines:
Configurable
Reusable across environments
Task Execution Order
runAfter:
- fetch-source
This ensures tasks run sequentially in the correct order.
4️⃣ Tekton PipelineRun – Executing the Pipeline
What is a PipelineRun?
A PipelineRun triggers the actual execution of a pipeline.
Think of it as:
Pipeline → Blueprint
PipelineRun → Actual run
Example PipelineRun
kind: PipelineRun
spec:
pipelineRef:
name: nodejs-cicd-pipeline
This tells Tekton which pipeline to execute.
Passing Runtime Parameters
params:
- name: git-url
value: "https://github.com/nodeshift-starters/nodejs-rest-http"
- name: app-name
value: "my-nodejs-app"
Parameters can be overridden without changing the pipeline definition.
5️⃣ Understanding emptyDir in PipelineRun
What is emptyDir?
emptyDir is a Kubernetes volume type.
In Tekton PipelineRun:
It creates temporary shared storage
Storage exists only for the lifetime of the PipelineRun
Automatically deleted after execution
workspaces:
- name: shared-data
emptyDir: {}
Why emptyDir Is Ideal for CI/CD
No storage provisioning required
Fast and lightweight
Perfect for short-lived pipelines
Most CI/CD pipelines do not require persistent data.
Step 2: Create a Project
oc project demo-app
Step 3: Create a Tekton Task
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: hello-world
spec:
params:
- name: message
type: string
default: "Hello from Tekton!"
steps:
- name: echo-message
image: registry.redhat.io/ubi8/ubi-minimal:latest
command:
- echo
args:
- "$(params.message)"
- name: show-date
image: registry.redhat.io/ubi8/ubi-minimal:latest
command:
- date
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: git-clone-task
spec:
params:
- name: url
type: string
- name: revision
type: string
default: "main"
workspaces:
- name: output
steps:
- name: clone
image: registry.redhat.io/ubi8/ubi:latest
script: |
#!/bin/bash
yum install -y git
cd $(workspaces.output.path)
git clone $(params.url) .
git checkout $(params.revision)
echo "Source code cloned successfully"
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: nodejs-build-test
spec:
workspaces:
- name: source
steps:
- name: install-dependencies
image: registry.access.redhat.com/ubi8/nodejs-16:latest
workingDir: $(workspaces.source.path)
script: |
#!/bin/bash
echo "Installing Node.js dependencies..."
npm install
echo "Dependencies installed successfully"
- name: run-tests
image: registry.access.redhat.com/ubi8/nodejs-16:latest
workingDir: $(workspaces.source.path)
script: |
#!/bin/bash
echo "Running tests..."
npm test || echo "No tests found, skipping..."
echo "Tests completed"
- name: build-application
image: registry.access.redhat.com/ubi8/nodejs-16:latest
workingDir: $(workspaces.source.path)
script: |
#!/bin/bash
echo "Building Node.js application..."
npm run build || echo "No build script found, skipping..."
echo "Build completed successfully"
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: build-push-image
spec:
params:
- name: IMAGE
type: string
- name: DOCKERFILE
type: string
default: "Dockerfile"
workspaces:
- name: source
steps:
- name: build-and-push
image: registry.redhat.io/rhel8/buildah:latest
workingDir: $(workspaces.source.path)
securityContext:
privileged: true
script: |
#!/bin/bash
echo "Building container image..."
# Create Dockerfile if it doesn't exist
if [ ! -f "$(params.DOCKERFILE)" ]; then
echo "Creating default Dockerfile..."
cat > Dockerfile << EOF
FROM registry.access.redhat.com/ubi8/nodejs-16:latest
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
EOF
fi
buildah bud -t $(params.IMAGE) -f $(params.DOCKERFILE) .
buildah push $(params.IMAGE)
echo "Image built and pushed: $(params.IMAGE)"
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: deploy-to-openshift
spec:
params:
- name: APP_NAME
type: string
- name: IMAGE
type: string
steps:
- name: deploy
image: registry.redhat.io/openshift4/ose-cli:latest
script: |
#!/bin/bash
set -e
echo "Deploying Node.js application to OpenShift..."
# Create deployment
cat <<EOF | oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: $(params.APP_NAME)
spec:
replicas: 1
selector:
matchLabels:
app: $(params.APP_NAME)
template:
metadata:
labels:
app: $(params.APP_NAME)
spec:
containers:
- name: app
image: gcr.io/google-samples/hello-app:1.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: $(params.APP_NAME)
spec:
selector:
app: $(params.APP_NAME)
ports:
- port: 8080
targetPort: 8080
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: $(params.APP_NAME)
spec:
to:
kind: Service
name: $(params.APP_NAME)
port:
targetPort: 8080
EOF
# Wait for deployment
oc rollout status deployment/$(params.APP_NAME) --timeout=300s
# Get route URL
ROUTE_URL=$(oc get route $(params.APP_NAME) -o jsonpath='{.spec.host}')
echo "Application deployed successfully!"
echo "Access URL: http://$ROUTE_URL"
oc apply -f /home/rhel/tekton-blog-demo/all-tasks-manifest.yaml
Step 4: Create a Tekton Pipeline
This pipeline uses the hello-task.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: nodejs-cicd-pipeline
spec:
params:
- name: git-url
type: string
default: "https://github.com/nodeshift-starters/nodejs-rest-http"
- name: git-revision
type: string
default: "main"
- name: app-name
type: string
default: "nodejs-app"
workspaces:
- name: shared-data
tasks:
# 1. Clone source code
- name: fetch-source
taskRef:
name: git-clone-task
workspaces:
- name: output
workspace: shared-data
params:
- name: url
value: $(params.git-url)
- name: revision
value: $(params.git-revision)
# 2. Build and test Node.js application
- name: build-test
taskRef:
name: nodejs-build-test
runAfter:
- fetch-source
workspaces:
- name: source
workspace: shared-data
# 3. Deploy to OpenShift
- name: deploy-app
taskRef:
name: deploy-to-openshift
runAfter:
- build-test
params:
- name: APP_NAME
value: $(params.app-name)
- name: IMAGE
value: "gcr.io/google-samples/hello-app:1.0"
Apply the pipeline:
oc apply -f /home/rhel/tekton-blog-demo/nodejs-cicd-pipeline.yaml
Step 5: Run the Pipeline
What the ServiceAccount Controls in a Pipeline
A ServiceAccount determines:
1️⃣ Permissions (RBAC)
What the pipeline pods can do:
Create deployments
Create services or routes
Read/write Kubernetes resources
Access the OpenShift API
oc adm policy add-scc-to-user privileged -z pipeline
Step 6: Run the Pipeline
Create a PipelineRun:
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: nodejs-cicd-run-
spec:
pipelineRef:
name: nodejs-cicd-pipeline
params:
- name: git-url
value: "https://github.com/nodeshift-starters/nodejs-rest-http"
- name: app-name
value: "my-nodejs-app"
workspaces:
- name: shared-data
emptyDir: {}
Apply it:
oc create -f nodejs-cicd-pipelinerun.yaml
Step 7: Monitor Pipeline Execution
Using OpenShift Console
Go to Pipelines → Pipelines
Select
hello-pipelineView execution graph and logs
Using CLI
oc get pipelineruns
oc logs -l tekton.dev/pipelineRun=hello-pipeline-run --all-containers
Expected output:
Hello from Tekton on OpenShift!
Step 8: Visualizing Pipelines in OpenShift
OpenShift provides a graphical pipeline view:
Task dependencies
Execution status
Logs per step
Duration metrics
This makes debugging and monitoring extremely easy compared to traditional CI tools.
Real‑World Use Case Example
In real projects, Tekton pipelines typically:
Clone source code from Git
Run unit tests
Build container images
Push images to OpenShift Image Registry
Deploy applications using
kubectloroc
Best Practices
Use Workspaces instead of emptyDir for shared data
Avoid hardcoding secrets – use Kubernetes Secrets
Version control pipeline YAMLs
Reuse tasks from Tekton Hub
Use Triggers for GitOps workflows
Common Troubleshooting Tips
Check SCC permissions if pods fail
Verify service account RBAC
Inspect TaskRun logs
Ensure correct API version (
tekton.dev/v1)
Conclusion
Tekton Pipelines on OpenShift provide a powerful, Kubernetes‑native CI/CD solution that integrates seamlessly with modern DevOps workflows. With operator‑based installation and a rich UI, OpenShift makes Tekton easy to adopt and operate.
If you’re building cloud‑native applications on OpenShift, Tekton is a must‑have tool in your DevOps toolkit.

