If you’ve managed traditional, centralized CI/CD platforms, you have likely run into a common architectural mismatch when moving to cloud-native infrastructure. Older, server-based systems were fundamentally built around persistent state and static build nodes. They do an excellent job in virtual machine environments, but as your development teams grow and cluster demand spikes, scaling a centralized controller server becomes a complex project of its own. You often end up dealing with resource bottlenecks during peak deployment hours and idle compute waste when the system is quiet.
OpenShift Pipelines offers a different approach, providing a truly cloud-native CI/CD solution. Instead of relying on a central server to orchestrate jobs, it integrates directly into the Kubernetes framework. Every step of your build and deploy process runs in its own ephemeral container. It spins up exactly when needed, executes its task, and disappears. This gives you independent scaling, precise resource management, and a declarative setup that lives entirely in Git alongside your code.
This article is designed for DevOps engineers and architects who need to transition to a declarative, container-native workflow that integrates directly into the OpenShift console. We move past the theory to focus on the practical mechanics of modular tasks and automated triggers to help you build a production-ready delivery system.
Summary of key best practices for OpenShift Pipelines
The following table summarizes the key OpenShift Pipelines best practices discussed in this article.
|
Best practice |
Description |
|
Standardize reusable tasks |
Build modular, single-purpose Tasks (like build or scan logic) that can be dropped into any pipeline without duplicating code. |
|
Implement event-driven automation |
Move past manual Tekton commands by using EventListeners and Triggers to kick off builds automatically. |
|
Keep secrets out of your YAML |
Link annotated Kubernetes Secrets to dedicated ServiceAccounts to handle private registry auth without hardcoding keys in YAML. |
|
Make security a “hard stop” |
Configure security tools like Trivy to fail the pipeline immediately if critical vulnerabilities are found, preventing insecure deployments. |
|
Persist audit metadata |
Use the Results API to store scan statuses and image digests in the cluster’s permanent datastore rather than relying on ephemeral pod logs. |
|
Use dedicated ServiceAccounts |
Use specific ServiceAccounts for pipeline execution to maintain granular control over permissions and access. |
Automated Application-Centric Red Hat OpenShift Data Protection & Intelligent Recovery
Why OpenShift needs a native CI/CD solution
Kubernetes fundamentally changed how companies ship code, but the CI/CD tools they use haven’t always kept up. If you try to force a legacy, server-based delivery tool into a cluster, it results in a lot of friction and manual configuration.
OpenShift Pipelines, based on the open-source Tekton project, is the fix for this problem. Instead of being an external tool bolted on, it’s built on Tekton and operates as a native cluster service. By using standard Kubernetes CRDs, your pipelines become part of the platform itself, inheriting the same security and management policies you’re already using for your production workloads.
This architectural shift addresses several fundamental challenges in modern DevOps:
- Scalability and resource efficiency: In older models, you often have to maintain static build nodes that sit idle, consuming CPU and memory even when no code is being built. Because OpenShift Pipelines is based on Tekton, it uses an “on-demand” model. Each step of your pipeline runs in its own ephemeral container. When the work is complete, the pod is terminated, and its resources are immediately returned to the cluster pool. This guarantees that you only use what you need, exactly when you need it.
- The “everything-as-code” paradigm: One of the primary benefits of a Tekton-based system is its declarative nature. Your entire pipeline, from the individual tasks to the overarching workflow, is defined in YAML. This eliminates the “Snowflake” problem where build configurations live in an opaque database or UI. Instead, your CI/CD infrastructure is version-controlled, peer-reviewed, and managed via the same GitOps processes as your production code.
- Security and isolation: Standardizing on Kubernetes RBAC is where native pipelines really shine. In the old way of doing things, you’d have one giant, centralized engine with “god-mode” permissions across your whole cluster. With OpenShift Pipelines, you can ditch that risk. You run each pipeline under its own dedicated ServiceAccount. This lets you box in build processes to specific namespaces and hammer home the principle of least privilege. Your CI/CD perms end up being just as tight as the production environment they’re deploying to.
Understanding the architecture of OpenShift Pipelines
OpenShift Pipelines relies on several foundational Tekton building blocks. To build a functional CI/CD system here, you need to understand how these individual components interact to form a complete workflow.
Here is a breakdown of the Tekton objects you will be managing in your manifests:
- Step: This is the smallest unit of execution in OpenShift Pipelines. It is a container running a specific command.
- Task: A sequential collection of Steps. A Task executes as a single Pod, meaning that all steps share the same local environment.
- Pipeline: A directed acyclic graph (DAG) of Tasks. This defines the overall workflow and the flow of data between stages.
- TaskRun and PipelineRun: Tasks and Pipelines are just static templates. A TaskRun executes a single Task, while a PipelineRun executes an entire Pipeline. These are the active objects that bind your generic templates to specific Git repos, image registries, and runtime parameters.
The ephemeral pod problem and workspaces
Since Tasks map to Pods, you’re working in a stateless environment. These containers spin down the moment they finish, wiping any local data. If your first Task pulls a repository and the second needs to compile it, the second Pod can’t access the first one’s disk. Once the clone step is done, the cluster kills that Pod and its filesystem. You have to hand off that source code before the container disappears.
This is where Workspaces can help. A workspace is just an abstraction over a PersistentVolumeClaim (PVC). You define it in the Pipeline, and Tekton mounts that same PVC into every Pod in the sequence. This provides a shared, persistent disk that survives the Pod lifecycle, letting you pass data from one stage to the next.
Steps to deploying OpenShift Pipelines
One neat thing about working with OpenShift is that everything comes packaged as an operator, and OpenShift Pipelines is no different. To start with the demo, we’ll install the OpenShift Pipelines operator from the software catalog.
Prerequisites
Before proceeding with the install:
- Make sure that you have access to the OpenShift Container Platform cluster and with cluster-admin privileges.
- In addition to the “oc” CLI, you need the Tekton CLI (tkn) to work with Tekton. The “tkn” CLI can be downloaded from here.
After extracting the archive, make sure that the location of the “tkn” binary is present in the PATH variable.
$ tar xvzf tkn-linux-amd64.tar.gz
$ echo $PATH
Installing the OpenShift Pipelines operator
Follow these steps:
- Access OperatorHub: In the Administrator perspective, navigate to Ecosystem > Software Catalog.
- Locate the Operator: Use the search bar to find Red Hat OpenShift Pipelines. Click the tile and then select Install.
- Configure installation settings:
- Installation mode: Choose all namespaces on the cluster. This makes the Operator active globally across your environment.
- Approval strategy: Select Automatic to allow the Operator Lifecycle Manager (OLM) to manage future updates without manual intervention.
- Update channel: Select the latest channel to run the most recent stable version. If your project requires a specific environment, you can select a versioned channel instead.
- Finalize: Click Install and wait for the status to change to “Succeeded.”
Once the installation is complete, verify that the Tekton controller pods are running. The Operator automatically sets up the global configuration and default storage settings.
$ oc get all -n openshift-pipelines
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
NAME READY STATUS RESTARTS AGE
pod/pipelines-as-code-controller-6b558d64d5-5rwcm 1/1 Running 1 5m
pod/pipelines-as-code-watcher-565468b9c5-sgsh6 1/1 Running 1 5m
pod/pipelines-as-code-webhook-7ff9f844cc-9qgk6 1/1 Running 1 5m
pod/pipelines-console-plugin-5b9c658745-8w9x5 1/1 Running 1 5m
pod/tekton-chains-controller-69b9f88cbb-4mmjg 1/1 Running 1 5m
pod/tekton-events-controller-7cc94db76d-mrfhc 1/1 Running 1 5m
pod/tekton-operator-proxy-webhook-8b648679d-gld7s 1/1 Running 1 5m
pod/tekton-pipelines-controller-7db895d4cc-j9z66 1/1 Running 1 5m
pod/tekton-pipelines-remote-resolvers-cd7ff8dfd-blp6p 1/1 Running 1 5m
pod/tekton-pipelines-webhook-6d5b676c-9bslm 1/1 Running 1 5m
[.................]
OpenShift Pipelines often include a set of preinstalled Tasks for common operations such as S2I builds, Git cloning, and container image management. All of these tasks are available in the openshift-pipelines project.
$ tkn task ls -n openshift-pipelines
NAMESPACE NAME DESCRIPTION AGE
openshift-pipelines argocd-task-sync-and-wait This task syncs (de... 1 day ago
openshift-pipelines buildah
Buildah task build... 1 day ago
openshift-pipelines buildah-1-22-0
Buildah task build... 1 day ago
openshift-pipelines buildah-ns
[...............]
You can get more information about a task as follows.
$ tkn -n openshift-pipelines task describe buildah
Name: buildah
Namespace: openshift-pipelines
Description:
Buildah task builds source into a container image and
then pushes it to a container registry.
Version: 0.8.0
Annotations:
artifacthub.io/category=integration-delivery
artifacthub.io/maintainers=- name: OpenShift Pipeline task maintainers
email: pipelines-extcomm@redhat.com
artifacthub.io/provider=Red Hat
artifacthub.io/recommendations=- url: https://tekton.dev/
[..................]
Params
NAME TYPE DESCRIPTION DEFAULT VALUE
∙ IMAGE string Fully qualified con... ---
∙ DOCKERFILE string Path to the `Docker... ./Dockerfile
∙ BUILD_ARGS array Dockerfile build ar... []
∙ CONTEXT string Path to the directo... .
∙ STORAGE_DRIVER string Set buildah storage... vfs
∙ FORMAT string The format of the b... oci
∙ BUILD_EXTRA_ARGS string Extra parameters pa...
∙ PUSH_EXTRA_ARGS string Extra parameters pa...
∙ SKIP_PUSH string Skip pushing the im... false
∙ TLS_VERIFY string Sets the TLS verifi... true
∙ VERBOSE string Turns on verbose lo... false
[................]
Automated Red Hat OpenShift Data Protection & Intelligent Recovery
Perform secure application-centric backups of containers, VMs, helm & operators
Use pre-staged snapshots to instantly test, transform, and restore during recovery
Scale with fully automated policy-driven backup-and-restore workflows
Writing your first Pipeline
Let’s write our first “Hello World” pipeline to get things up and running. This Pipeline involves defining a reusable Task, wrapping it in a Pipeline, and finally executing it with a PipelineRun.
Start by creating the Task definition for the demo pipeline.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: hello-world-task
spec:
steps:
- name: say-hello
image: registry.access.redhat.com/ubi9/ubi-micro
script: |
#!/usr/bin/env bash
echo "Hello, World! This is running on OpenShift Pipelines."
Apply the manifest.
$ oc apply -f hello-task.yaml
task.tekton.dev/hello-world-task created
A Pipeline connects one or more tasks. Even for a “Hello World” demo, this abstraction is necessary to show the workflow. Create the Pipeline as follows.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: hello-world-pipeline
spec:
tasks:
- name: hello-task-reference
taskRef:
name: hello-world-task
Apply the manifest.
$ oc apply -f hello-pipeline.yaml
pipeline.tekton.dev/hello-world-pipeline created
The pipeline run can also be triggered with the “oc” command after creating a PipelineRun object, but we’ll use the “tkn” binary downloaded earlier.
$ tkn pipeline start hello-world-pipeline --showlog
PipelineRun started: hello-world-pipeline-run-vg6mt
Waiting for logs to be available...
[hello-task-reference : say-hello] Hello, World! This is running on OpenShift Pipelines.

Advanced automation with Tekton Triggers and event handling
Manual testing works for a start, but real-world CI/CD needs to be event-driven. By using Tekton Triggers, your OpenShift cluster can listen for Git pushes or Pull Requests and automatically kick off pipelines, shifting your workflow from manual execution to a true cloud-native automation model.
Core components of automation
To transition from manual to automated execution, four specific Tekton objects are required:
- EventListener: A long-running pod that exposes an HTTP endpoint (“sink”) to receive incoming webhooks from platforms like GitHub or GitLab.
- Trigger: The logic that connects the EventListener to specific bindings and templates.
- TriggerBinding: A mapping tool that extracts specific data from the incoming JSON payload (e.g., commit SHAs or repository URLs) and converts it into parameters.
- TriggerTemplate: A blueprint that defines the resource to be created (typically a PipelineRun) and specifies how to inject the parameters extracted by the binding.
Demo: An environment-agnostic automated workflow
In a standard production setup, a Git server automatically sends a webhook to the cluster. However, to ensure that this demo is functional in any environment, including local instances where external connectivity might be restricted, we will use a Simulated Trigger approach.
Instead of relying on an external Git provider, we will use a “curl” command to send a JSON payload directly to the internal listener. This mimics the exact behavior of a Git webhook, allowing you to validate the automation logic without the overhead of networking tunnels.
The following demo builds on the foundational “Hello World” setup from the previous demo. By reusing the existing Task and Pipeline definitions, we transition the manual workflow to a fully automated, event-driven system.
The TriggerBinding captures the repository URL from our simulated payload.
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
name: internal-push-binding
spec:
params:
- name: git-repo-url
value: $(body.repository.url)
Create the binding.
$ oc apply -f git-binding.yaml
triggerbinding.triggers.tekton.dev/internal-push-binding created
Define the TriggerTemplate, which tells Tekton to create a PipelineRun whenever the trigger is fired.
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
name: internal-push-template
spec:
params:
- name: git-repo-url
resourcetemplates:
- apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: automated-hello-run-
spec:
pipelineRef:
name: hello-world-pipeline
Create the template.
$ oc apply -f git-template.yaml
triggertemplate.triggers.tekton.dev/internal-push-template created
Create an EventListener, which will remain idle until it receives an HTTP request.
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: internal-listener
spec:
triggers:
- name: internal-trigger
bindings:
- ref: internal-push-binding
template:
ref: internal-push-template
Create the event listener.
$ oc apply -f git-listener.yaml
eventlistener.triggers.tekton.dev/internal-listener created
You can list down the created EventListener, triggerTemplate and triggerBinding as follows.
$ tkn eventlistener list
NAME AGE URL AVAILABLE
internal-listener 1 hour ago http://el-internal-listener.p1.svc.cluster.local:8080 True
$ tkn triggerbinding list
NAME AGE
internal-push-binding 1 hour ago
$ tkn triggertemplate list
NAME AGE
internal-push-template 1 hour ago
$ tkn triggertemplate describe internal-push-template
Name: internal-push-template
Namespace: p1
⚓ Params
NAME DESCRIPTION DEFAULT VALUE
∙ git-repo-url ---
📦 ResourceTemplates
NAME GENERATENAME KIND APIVERSION
∙ --- automated-hello-run- PipelineRun tekton.dev/v1beta1
Expose the service to the network to allow the simulation command to reach the listener.
$ oc expose svc el-internal-listener
route.route.openshift.io/el-internal-listener exposed
$ LISTENER_URL=$(oc get route el-internal-listener -o jsonpath='{.spec.host}')
Run the following curl command to trigger the automation sequence. This sends the JSON data that the TriggerBinding is configured to extract.
$ url -v -X POST http://$LISTENER_URL -H "Content-Type: application/json" -d '{
"repository": {
"url": "https://internal-git.example.com/demo-app.git"
}
}'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Host el-internal-listener-p1.apps-crc.testing:80 was resolved.
* IPv6: (none)
* IPv4: 127.0.0.1
* Trying 127.0.0.1:80...
* Established connection to el-internal-listener-p1.apps-ocp.local (127.0.0.1 port 80) from 127.0.0.1 port 34170
* using HTTP/1.x
> POST / HTTP/1.1
> Host: el-internal-listener-p1.apps-ocp.local
> User-Agent: curl/8.18.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 92
>
* upload completely sent off: 92 bytes
< HTTP/1.1 202 Accepted
< content-type: application/json
< date: Mon, 04 May 2026 16:04:04 GMT
< content-length: 162
< set-cookie: 101759d7115e46e889c337e10d952446=976990b496f911409694d1e74637c31a; path=/; HttpOnly
[.............]
Confirm that the automation successfully instantiated the pipeline:
$ tkn pipelinerun list
NAME STARTED DURATION STATUS
automated-hello-run-ptskz 8 seconds ago --- Running
$ tkn pipelinerun logs automated-hello-run-ptskz
[hello-task-reference : say-hello] Hello, World! Automation successful.
$ tkn pipelinerun list
NAME STARTED DURATION STATUS
automated-hello-run-ptskz 13 seconds ago 12s Succeeded
This demonstrates how Tekton Triggers catch live metadata and put it to work instantly. It’s the shift from running manual commands to building a system that actually reacts to your code changes in real time.
Pipeline security and supply chain integrity
In a modern cloud-native engineering workflow, automating the build is only half the battle. Ensuring artifact security and validation compliance is a critical requirement for any production-ready pipeline.
Image scanning with Trivy
To protect supply chain integrity, we implement security “gates” using Trivy, a lightweight vulnerability scanner. Using Trivy vets images for vulnerabilities before they are promoted or deployed.
We create a dedicated security Task that scans a container image for “High” or “Critical” vulnerabilities. If the scanner detects these risks, it will exit with a non-zero code, effectively failing the pipeline and preventing the deployment of insecure code.
This task uses the Trivy image to inspect a target image and generates a failure if it finds severe vulnerabilities.
Start by defining and creating the Trivy scan task as follows.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: trivy-scan
spec:
params:
- name: image-to-scan
description: Public image to scan
default: "node:18-slim"
steps:
- name: scan
image: aquasec/trivy:latest
command: ["trivy"]
args:
- image
- "--severity"
- "HIGH,CRITICAL"
- "--exit-code"
- "1"
- $(params.image-to-scan)
$ oc apply -f trivy-scan-task.yaml
task.tekton.dev/trivy-scan created
Create a pipeline as shown below. The pipeline is configured to pass a parameter down to the task and uses runAfter to restrict core workload execution to instances where the security scan passes.
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: secured-hello-pipeline
spec:
params:
- name: image-to-scan
type: string
default: "node:18-slim"
tasks:
- name: security-scan
taskRef:
name: trivy-scan
params:
- name: image-to-scan
value: $(params.image-to-scan) # Passes pipeline param to task param
- name: hello-task
runAfter: ["security-scan"]
taskRef:
name: hello-world-task
$ oc apply -f secured-pipeline.yaml
To understand how Trivy protects your environment, we can look at two specific execution scenarios.
Scenario 1: Vulnerability detected
If we scan an image with known vulnerabilities (such as “node:18-slim”), Trivy identifies critical risks and forces the task to fail.
$ tkn pipeline start secured-hello-pipeline --param image-to-scan="node:18-slim" --showlog
PipelineRun started: secured-hello-pipeline-run-qq2x8
Waiting for logs to be available...
[security-scan : scan] 2026-05-04T16:53:48Z INFO [vulndb] Need to update DB
[security-scan : scan] 2026-05-04T16:53:48Z INFO [vulndb] Downloading vulnerability DB...
[security-scan : scan] 2026-05-04T16:53:48Z INFO [vulndb] Downloading artifact... repo="mirror.gcr.io/aquasec/trivy-db:2"
[...........]
[security-scan : scan] ┌──────────────────────────────────────────────────────────────────────────────────┬──────────┬─────────────────┬─────────┐
[security-scan : scan] │ Target │ Type │ Vulnerabilities │ Secrets │
[security-scan : scan] ├──────────────────────────────────────────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤
[security-scan : scan] │ node:18-slim (debian 12.11) │ debian │ 21 │ - │
[security-scan : scan] ├────────────────────────────────────────────────────────
[............]
[security-scan : scan] node:18-slim (debian 12.11)
[security-scan : scan] ===========================
[security-scan : scan] Total: 21 (HIGH: 20, CRITICAL: 1)
[..........]
[security-scan : scan] ┌────────────────────┬────────────────┬──────────┬──────────────┬───────────────────┬────────────────────┬──────────────────────────────────────────────────────────────┐
[security-scan : scan] │ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
[security-scan : scan] ├────────────────────┼────────────────┼──────────┼──────────────┼───────────────────┼────────────────────┼──────────────────────────────────────────────────────────────┤
[security-scan : scan] │ gpgv │ CVE-2025-68973 │ HIGH │ fixed │ 2.2.40-1.1 │ 2.2.40-1.1+deb12u2 │ GnuPG: GnuPG: Information disclosure and potential arbitrary │
[..................]
failed to get logs for task security-scan : container step-scan has failed : [{"key":"StartedAt","value":"2026-05-04T16:53:47.803Z","type":3}]
Scenario 2: Compliant image
When we use a highly secure, minimal image like the Red Hat Universal Base Image (UBI), the scanner finds no critical policy violations.
$ tkn pipeline start secured-hello-pipeline --param image-to-scan="registry.access.redhat.com/ubi9/ubi-micro" --showlog
PipelineRun started: secured-hello-pipeline-run-2c5df
Waiting for logs to be available...
[security-scan : scan] 2026-05-04T17:01:36Z INFO [vulndb] Need to update DB
[security-scan : scan] 2026-05-04T17:01:36Z INFO [vulndb] Downloading vulnerability DB...
[security-scan : scan] 2026-05-04T17:01:36Z INFO [vulndb] Downloading artifact... repo="mirror.gcr.io/aquasec/trivy-db:2"
[............]
[security-scan : scan] 2026-05-04T17:03:18Z INFO [vuln] Vulnerability scanning is enabled
[security-scan : scan] 2026-05-04T17:03:18Z INFO [secret] Secret scanning is enabled
[security-scan : scan] 2026-05-04T17:03:18Z INFO [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
[security-scan : scan] 2026-05-04T17:03:18Z INFO [secret] Please see https://trivy.dev/docs/v0.70/guide/scanner/secret#recommendation for faster secret detection
[security-scan : scan] 2026-05-04T17:03:30Z INFO Detected OS family="redhat" version="9.7"
[security-scan : scan] 2026-05-04T17:03:30Z INFO [redhat] Detecting RHEL/CentOS vulnerabilities... os_version="9" pkg_num=20
[security-scan : scan] 2026-05-04T17:03:30Z INFO Number of language-specific files num=0
[security-scan : scan] 2026-05-04T17:03:30Z WARN Using severities from other vendors for some vulnerabilities. Read https://trivy.dev/docs/v0.70/guide/scanner/vulnerability#severity-selection for details.
[security-scan : scan]
[security-scan : scan] Report Summary
[security-scan : scan]
[security-scan : scan] ┌────────────────────────────────────────────────────────┬────────┬─────────────────┬─────────┐
[security-scan : scan] │ Target │ Type │ Vulnerabilities │ Secrets │
[security-scan : scan] ├────────────────────────────────────────────────────────┼────────┼─────────────────┼─────────┤
[security-scan : scan] │ registry.access.redhat.com/ubi9/ubi-micro (redhat 9.7) │ redhat │ 0 │ - │
[security-scan : scan] └────────────────────────────────────────────────────────┴────────┴─────────────────┴─────────┘
[security-scan : scan] Legend:
[security-scan : scan] - '-': Not scanned
[security-scan : scan] - '0': Clean (no security findings detected)
[security-scan : scan]
[hello-task : say-hello] Hello, World! Automation successful.
Persisting Audit Metadata with Tekton Results
To store, build, and persist security metadata, Tekton provides the Results API. Unlike logs, which can be voluminous and ephemeral, Results allow you to store small, structured pieces of data, like a “COMPLIANT” status or an image digest, that remain attached to the TaskRun or PipelineRun long after the execution pod has been deleted.
Define the results object and add a step to write the data to the automatically generated result path.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: trivy-scan
spec:
params:
- name: image-to-scan
description: Public image to scan
default: "node:18-slim"
type: string
results:
- name: scan-status
description: Indicates if the scan passed or failed.
type: string
steps:
- name: scan
image: aquasec/trivy:latest
command: ["trivy"]
args:
- image
- "--severity"
- "HIGH,CRITICAL"
- "--exit-code"
- "1"
- $(params.image-to-scan)
- name: write-result
image: registry.access.redhat.com/ubi9/ubi-micro
script: |
#!/usr/bin/env bash
# Only reached if the previous 'scan' step succeeds
echo "COMPLIANT" > $(results.scan-status.path)
Create the updated task and execute the same pipeline again. You can now retrieve the structured audit metadata directly.
$ tkn taskrun describe --last
Name: secured-hello-pipeline-run-v9q4l-hello-task
Namespace: p1
Task Ref: hello-world-task
Service Account: pipeline
Timeout: 1h0m0s
Labels:
app.kubernetes.io/managed-by=tekton-pipelines
tekton.dev/memberOf=tasks
tekton.dev/pipeline=secured-hello-pipeline
tekton.dev/pipelineRun=secured-hello-pipeline-run-v9q4l
tekton.dev/pipelineRunUID=fb95155c-6e43-4769-a395-8c79199e18f6
tekton.dev/pipelineTask=hello-task
tekton.dev/task=hello-world-task
Annotations:
chains.tekton.dev/signed=true
pipeline.tekton.dev/release=52a824a1a459db1461390074ce2f1ac438aa3c32
results.tekton.dev/childReadyForDeletion=true
results.tekton.dev/record=p1/results/fb95155c-6e43-4769-a395-8c79199e18f6/records/a901fcab-b643-45df-b6bb-f32b1817cdc4
results.tekton.dev/result=p1/results/fb95155c-6e43-4769-a395-8c79199e18f6
results.tekton.dev/stored=true
🌡️ Status
STARTED DURATION STATUS
27 seconds ago 18s Succeeded
🦶 Steps
NAME STATUS
∙ say-hello Completed
In enterprise environments, you’ll likely need to pull from private registries or clone from private Git repositories. You can handle authentication without changing a single line of your YAML by linking Kubernetes Secrets to your ServiceAccount.
You can define your credentials in a standard Docker registry and tell Tekton which registry this secret belongs to and attach it to the pipeline ServiceAccount.
$ oc create secret docker-registry private-registry-creds \
--docker-server=registry.redhat.io \
--docker-username= \
--docker-password=
$ oc create sa demo-pipeline-sa
$ oc annotate secret private-registry-creds "tekton.dev/docker-0=quay.io"
$ oc secrets link demo-pipeline-sa private-registry-creds --for=pull,mount
$ tkn pipeline start secured-hello-pipeline \
--param image-to-scan="registry.redhat.io/ubi9/ubi-micro" \
--serviceaccount demo-pipeline-sa \
--showlog
Ensuring Pipeline Resilience and Recovery
Once your pipeline handles security gates and triggers, the final step is to protect that logic. The Tekton objects and Custom Resource Definitions (CRDs) reside directly in etcd, which means they, like all other API objects, are also vulnerable to cluster-wide disruptions. A botched cluster upgrade or an accidental namespace deletion can instantly wipe out the entire CI/CD delivery framework.
For this purpose, you can consider a platform like Trilio. Trilio checkpoints the exact automation state by capturing the declarative pipeline objects alongside the live application state. This means if you need to migrate your workloads or recover from an outage, your entire declarative pipeline, not just the resulting container, is restored in its last known good state.
Recommended practices for OpenShift Pipelines
Based on the core components and workflows implemented in this guide, adhere to the following practices for enterprise CI/CD orchestration:
- Standardize reusable tasks: Build a library of modular tasks, like your vulnerability scanner or build logic, so you can drop them into any pipeline without reinventing the wheel every time.
- Implement Least-Privilege ServiceAccounts: Default pipeline configurations often rely on over-privileged service accounts, creating unnecessary security risks. For production pipelines, create dedicated service accounts bound to specific, narrow roles. This restricts permissions to only the exact tasks required.
- Implement event-driven automation: Manual tkn commands are fine for testing, but real-world workflows need Tekton Triggers. This lets your cluster watch for Git pushes or PRs and trigger the automation.
- Keep secrets out of your YAML: Don’t hardcode credentials or API keys. Stick them in Kubernetes Secrets and tie them to your Pipeline’s ServiceAccount so they stay protected and out of your source code.
- Make security a “hard stop”: Treat your scan tasks like a real gate. If a tool like Trivy finds a critical bug, the pipeline should fail immediately, stopping insecure code from ever reaching your environment.
- Persist audit metadata: Pod logs disappear. Use the Results API to save scan statuses and image IDs directly to the cluster, so you actually have a permanent record when audit time rolls around.
Learn How To Best Backup & Restore Virtual Machines Running on OpenShift
Conclusion
Applying legacy CI/CD architectures to cloud-native platforms such as Kubernetes and OpenShift often imposes several constraints. The reason is that they were fundamentally built around static build nodes and a centralized controller. OpenShift Pipelines solves this problem by dropping the central server entirely.
OpenShift Pipelines removes the chronic resource bottlenecks and compute waste during peak deployment hours that plague older systems by eliminating the reliance on a single, persistent controller server. OpenShift Pipelines scales dynamically with the cluster, executing each build step within ephemeral containers that consume resources only when active.
With the practical framework for modular tasks and automated triggers established in this guide, a production-ready, declarative delivery system is now fully within reach. To scale this architecture further, the next logical step is adopting pipelines as code. This allows teams to store repository-level pipeline definitions directly alongside the application source code, driving the entire execution lifecycle straight from Git webhooks.
Like This Article?
Subscribe to our LinkedIn Newsletter to receive more educational content