Manage PAC Component
This guide is for cluster administrators only. It covers PAC component deployment, configuration, and maintenance tasks that require cluster administrator permissions.
Regular users should refer to:
- Guides - Set up Git provider integration
This guide explains how to deploy, update, and uninstall the Pipelines-as-Code (PAC) component on Kubernetes platforms.
TOC
PrerequisitesDeploy PAC ComponentConfigure AccessUsing Gateway APIUsing IngressUsing NodePortConfiguration SettingsStandard SettingsAlauda Extension SettingsWhere to Put Each SettingUpdate PAC ComponentUpdate ConfigurationCommon Configuration UpdatesConfigure Custom Console LinksConfigure Pull Request Number on Push EventsChange Application NameEnable Error DetectionUpdate Hub URLDisable Remote TasksUninstall PAC ComponentDelete theOpenShiftPipelinesAsCode CRClean up resources the operator does not ownTroubleshootingPAC Pods Not StartingOpenShiftPipelinesAsCode CR Not ReadyTektonInstallerSet IssuesCR Not DeletingResources Not RemovedNext StepsPrerequisites
Before managing PAC, ensure you have:
- Kubernetes cluster (version 1.24 or higher)
- Tekton Operator installed and running
- Cluster administrator permissions
- kubectl installed and configured to access your cluster
Deploy PAC Component
Create the OpenShiftPipelinesAsCode CR to deploy PAC. The examples use the default PAC namespace tekton-pipelines; replace it in the commands and manifests if you set a different targetNamespace.
Create a YAML file named pac.yaml:
hub-url is intentionally omitted: the operator already ships a working value in the
pipelines-as-code ConfigMap. Set it only to point at a different Hub — see
Standard Settings.
Apply the CR to your cluster:
Check the OpenShiftPipelinesAsCode CR status:
The output should show READY=True; VERSION varies by release and REASON is usually empty.
Verify the PAC pods are running:
Example output (the controller, watcher, and webhook pods must be Running):
Configure Access
The PAC controller must be reachable from the Git providers that will send webhook events to it. Expose it through one of the methods below before configuring any repository.
Using Gateway API
Use this method to expose the PAC controller with a domain name through ACP Gateway API.
This example uses:
- Default PAC namespace:
tekton-pipelines - GatewayClass:
envoy-gateway-operator-cpaas-default - Domain:
pac.example.com
Step 1: Prepare Envoy Gateway. Install Alauda build of Envoy Gateway and ensure the default GatewayClass is accepted. Reference: Envoy Gateway Operator.
Step 2: Prepare LoadBalancer addresses. The Envoy Gateway Service will be created as type: LoadBalancer, so LoadBalancer Services must be able to get an external IP. On ACP bare-metal clusters, install and configure Alauda Container Platform Load Balancer for MetalLB. Reference: Configure MetalLB.
Expected result:
Step 3: Create Gateway API resources. Create gateway-api.yaml. Replace tekton-pipelines, envoy-gateway-operator-cpaas-default, and pac.example.com as needed. References: Configure GatewayAPI Gateway and Configure GatewayAPI Route.
Apply the file:
Expected result:
Step 4: Get the external address. Check the generated Envoy Service:
Expected result:
Step 5: Verify and get the webhook URL. Make sure the Git provider can resolve and access the PAC domain. A common way is to create a DNS A record. For example, if the Service EXTERNAL-IP is 192.168.1.100, create:
If DNS is not ready yet, or you only want to test the route from your current machine, use curl --resolve:
Expected result:
- The Service has an
EXTERNAL-IP. - The Gateway shows
PROGRAMMED=True. - The
HTTPRouteis accepted. curlreturns the PAC controller response.
After the domain is reachable from the Git provider network, print the URL:
WEBHOOK_URL is the PAC webhook URL. Register this value in the Git provider or enter it when tkn pac create repo prompts for a webhook URL.
If you expose PAC through ACP ALB or another Ingress Controller instead, use Using Ingress.
Notes:
HTTPRouteforwards to the existingpipelines-as-code-controllerService on port8080; do not point it at the admission webhook Service namedpipelines-as-code-webhook.- If the generated Envoy Service remains
EXTERNAL-IP=<pending>, check the cluster LoadBalancer provider. For MetalLB, see Configure MetalLB. - For Gateway API options such as a reserved VIP, hostless routes, or HTTPS listeners, see Configure GatewayAPI Gateway and Configure GatewayAPI Route.
Using Ingress
Use this method when the cluster already has an Ingress Controller and you want to expose the PAC controller with an Ingress domain.
This example uses:
- Default PAC namespace:
tekton-pipelines - Domain:
pac.example.com
Step 1: Prepare an Ingress Controller. Ensure an Ingress Controller is installed and ready. Reference: Configure Ingress.
Step 2: Create the Ingress resource. Create ingress.yaml. Replace tekton-pipelines and pac.example.com as needed.
Apply the file:
Expected result:
Step 3: Verify the Ingress address. Check that the Ingress has an address:
Expected result:
Step 4: Get the webhook URL. Make sure the Git provider can resolve and access pac.example.com through the Ingress address. Then print the URL:
WEBHOOK_URL is the PAC webhook URL. Register this value in the Git provider or enter it when tkn pac create repo prompts for a webhook URL.
If you do not have a DNS name, remove the host field and use the reachable Ingress IP URL instead.
Optional: Enable HTTPS. Create a TLS Secret in tekton-pipelines and add a tls section to the same Ingress. The certificate must match pac.example.com.
When TLS is configured, use the HTTPS webhook URL:
Using NodePort
Create a NodePort Service:
Important:
- The
targetPortmust be8082, which is the port the PAC controller pod listens on for webhook events - The
port(8080) is the Service port (used for internal cluster communication) - The
nodePort(30080) is the external port accessible from outside the cluster - For Ingress, the Service port is 8080, which routes to the controller's port 8082 internally
Print the URL from a reachable node IP and the generated NodePort:
WEBHOOK_URL is the PAC webhook URL. Register this value in the Git provider or enter it when tkn pac create repo prompts for a webhook URL.
Configuration Settings
PAC configuration lives in two different places on the OpenShiftPipelinesAsCode CR: spec.settings and spec.options.configMaps. Which one you must use depends on the key. Read Where to Put Each Setting before editing, because a key written in the wrong place is dropped without any error.
Standard Settings
These keys go under spec.settings:
hub-url addresses the Hub the PAC controller queries when resolving remote tasks. The operator ships a working value, so leave all three hub settings unset unless you are pointing PAC at a different Hub. To see what your cluster actually uses:
If you point hub-url at a Tekton Hub instance, you must also set hub-catalog-type: tekton. The shipped default is artifacthub, and setting hub-url alone leaves it in place, so PAC would talk to a Tekton Hub using the ArtifactHub API and fail to resolve tasks.
custom-console-* settings rewrite the cluster-side links PAC posts back to the Git provider so they point at the platform console rather than at the OpenShift Console. The walkthrough is in Configure Custom Console Links.
Alauda Extension Settings
These keys are Alauda additions on top of upstream Pipelines as Code. They must go under spec.options.configMaps, not under spec.settings:
Walkthroughs: Configure Custom Console Links for custom-console-url-namespace-vars, and Configure Pull Request Number on Push Events for the other two.
Where to Put Each Setting
The two locations are handled by completely different code paths in the operator:
spec.settingsis not stored as you write it. The operator's defaulting webhook parses your map into the upstream Pipelines as Code settings struct and then regenerates the map from that struct. Any key that has no matching field in the struct — which is the case for every Alauda extension setting listed above — is silently discarded: the CR loses the key, the generatedpipelines-as-codeConfigMap never receives it, and no error or event is reported.spec.options.configMapsis applied by the additional-options transformer, which is the last transformer to run and merges your entries into the rendered ConfigMap key by key. Nothing rewrites this map afterwards, so any key survives — including keys the operator does not know about.
Three consequences worth remembering:
- Put upstream keys under
spec.settingsand Alauda extension keys underspec.options.configMaps. Do not move upstream keys intooptionswithout reason:settingsis the documented, validated location for them. - If the same key is set in both places, the
spec.options.configMapsvalue wins, because that transformer runs after the settings-derived ConfigMap has been rendered. - Setting
spec.options.disabled: trueturns the whole transformer off. Everything underspec.optionsis then ignored, including these settings, and you are back to the silent-drop behavior. Leave it unset orfalse.
spec.options.configMaps is a map keyed by ConfigMap name — it can target any ConfigMap in the component's manifest, and creates one if the name does not exist there. PAC reads its settings from the ConfigMap named pipelines-as-code, so that is the key to use here:
Verify that the key reached the ConfigMap. This is the only reliable check — reading it back from spec.settings does not tell you whether PAC received it:
Update PAC Component
Update Configuration
-
Edit the
OpenShiftPipelinesAsCodeCR: -
Update the
settingsfield as needed:To change one of the Alauda extension settings, edit
spec.options.configMapsinstead — those keys are removed fromspec.settingson save. -
Save and exit. The operator will automatically update the TektonInstallerSet and apply the changes.
Common Configuration Updates
The examples in this section update the OpenShiftPipelinesAsCode CR named pipelines-as-code. Each example shows the field it belongs in — most use spec.settings, and the Alauda extension settings use spec.options.configMaps. See Where to Put Each Setting if you are unsure which applies.
Configure Custom Console Links
The custom-console-* settings rewrite the cluster-side links PAC posts back to the Git provider so they point at the platform console. The example below resolves {{ project }} and {{ cluster }} from namespace labels, so the URLs do not hard-code a cluster identifier.
Note that the three templated keys live under spec.settings, while custom-console-url-namespace-vars must live under spec.options.configMaps — see Where to Put Each Setting:
Effect: Git provider status links open the platform console PipelineRun and task pages instead of the default OpenShift-style placeholder URLs.
custom-console-url is not a template
Only custom-console-url-pr-details, custom-console-url-pr-tasklog and custom-console-url-namespace go through template expansion. PAC returns custom-console-url verbatim, so writing {{ ... }} in it produces a broken link. Keep it a plain URL: it is the console entry point and also the value PAC falls back to whenever a templated URL fails to build.
PAC expands these variables when it posts status links:
The standard event variables ({{ revision }}, {{ repo_url }} and the rest) are also available in these templates, as are the Repository spec.params entries that resolved for this event — an entry with no name, or with a CEL filter that did not match, is not passed through. The five builtin names above always win: a namespace variable or a Repository parameter that reuses one of them is ignored rather than overriding it.
custom-console-url-namespace-vars is a comma-separated list. Each item uses name=label:<key> or name=annotation:<key>, with an optional |<default> fallback used when the label or annotation is missing or empty. Without a default the variable resolves to an empty string rather than being left as a literal {{ name }}.
Two things have to hold for that to happen. PAC must be able to read the namespace — if the Namespace GET fails, for example because the PAC controller has no permission on it, no namespace-derived variable is injected for that run. And the namespace it reads is the Repository namespace while the run is being created, and the PipelineRun namespace when the final status is reported; the two differ only if a PipelineRun is routed elsewhere through the target-namespace annotation. Whenever a variable ends up unsubstituted, generateURL discards the whole URL and falls back to custom-console-url, which is exactly the "the link is right but never expands" symptom.
Namespace labels and annotations are cached for up to 30 minutes, so label changes may take a few minutes to appear in generated links.
For a PipelineRun named my-app-build-abc123 in namespace my-app with labels cpaas.io/project=team-a and cpaas.io/cluster=prod, PAC generates links like:
These URLs appear in commit statuses, GitHub Checks panels and merge request comments.
Configure Pull Request Number on Push Events
{{ pull_request_number }} is a dynamic variable you can reference from a PipelineRun in your repository. It has an obvious value on a pull request event, but not on a push event. Two Alauda extension settings control what happens then. Both live under spec.options.configMaps, not under spec.settings:
enable-pull-request-number-on-push-events (default "true"): when a push event carries a commit that belongs to a pull request, PAC sets {{ pull_request_number }} to that pull request number. Set it to "false" if you want push events to never inherit a pull request number — for example when a pipeline uses the variable to decide whether it is running for a pull request.
This setting only affects GitHub. The other providers never populate {{ pull_request_number }} from a push event, so on GitLab and the rest a push event behaves as if the setting were "false".
It also only comes into play for push events that PAC still processes. The upstream skip-push-event-for-pr-commits setting defaults to "true", and it makes PAC drop a GitHub push event outright when the pushed commit belongs to an open pull request — no PipelineRun is created at all, so no variable is expanded. Everything else still reaches this setting: tag pushes, which the skip rule never applies to; pushes whose commit is associated only with closed or merged pull requests, which is the usual case for the push that results from a merge; and every push event once skip-push-event-for-pr-commits is set to "false".
replace-empty-template-vars-with-empty (default "false"): controls what PAC does with template variables it could not resolve. By default an unresolved {{ pull_request_number }} is left in the PipelineRun as the literal string {{ pull_request_number }}, which usually shows up as a nonsensical parameter value or a validation failure. Set it to "true" to replace unresolved variables with an empty string instead. Variables whose name starts with body, headers or files are deliberately left alone, because they are resolved by a different mechanism.
For a GitHub push event that PAC does process, the two combine as follows:
The table assumes nothing else provides that name. A Repository spec.params entry called pull_request_number is passed through like any other parameter, and it is what the template resolves to whenever PAC did not take the value from the event itself.
Effect: with replace-empty-template-vars-with-empty: "true", pipelines that reference {{ pull_request_number }} run unchanged on both pull request and push events instead of failing on push.
replace-empty-template-vars-with-empty applies to PipelineRun definitions resolved from the repository. It does not affect the custom-console-url-* templates, which have their own fallback behavior described in Configure Custom Console Links.
Change Application Name
Use this setting to change the display name that PAC uses in Git provider status messages:
Effect: Git provider checks, statuses, and comments show the new application name. This does not change the OpenShiftPipelinesAsCode resource name.
Enable Error Detection
Error detection is already on by default. Set it explicitly only when you want to change how many log lines are scanned, or to turn the feature off:
Effect: PAC scans failed task logs and adds a short error snippet to the Git provider feedback. This is a GitHub App feature; the other providers ignore it.
Update Hub URL
By default PAC resolves remote tasks from the in-cluster ArtifactHub Shim, and nothing needs to be configured. Override it only to point PAC at a different Hub — and always set hub-catalog-type together with hub-url, because the shipped artifacthub value stays in place otherwise:
Effect: PAC resolves remote tasks from the Hub you specify instead of the in-cluster shim.
To check the value currently in effect:
Example output:
Disable Remote Tasks
Use remote-tasks to control whether PAC fetches and embeds remote resources referenced by PAC annotations.
remote-tasks: "true" is the default. PAC can fetch remote resources referenced by pipelinesascode.tekton.dev/task and pipelinesascode.tekton.dev/pipeline annotations, then embed the resolved Task or Pipeline into the generated PipelineRun.
remote-tasks: "false" disables PAC annotation-based remote resource resolution. Pipeline code must define the required Pipeline and Tasks in the repository, inline them, or rely on cluster resources.
This setting does not disable Tekton Pipelines remote resolver syntax such as taskRef.resolver or pipelineRef.resolver; those are handled by the Tekton Pipelines controller.
Effect: Use "false" when you want to prevent PAC from fetching remote Tasks or Pipelines from annotation references.
Uninstall PAC Component
Delete the OpenShiftPipelinesAsCode CR
Removing the CR makes the operator tear down the TektonInstallerSet and every PAC Deployment, Service, ConfigMap and RBAC object the operator created.
Confirm the CR, the installer set and the pods are gone:
Each of these should return No resources found.
Clean up resources the operator does not own
The operator removes everything it created. Resources you added yourself stay behind and must be removed manually.
Repository CRs in user namespaces:
Per-repository Secrets in user namespaces. These are the Git provider access tokens (provider.token) and webhook secrets (webhook.secret) created by users when configuring repositories. The PAC-owned cluster secrets (carrying the app.kubernetes.io/part-of=pipelines-as-code label) are removed by the operator; these per-repository ones are not. Verify each Secret is not used by any other resource before deleting:
Gateway API resources for the PAC controller, if you created them in Configure Access:
Ingress / NodePort Service for the PAC controller, if you created them in Configure Access:
Troubleshooting
PAC Pods Not Starting
Check pod logs:
Example output (example log entries):
OpenShiftPipelinesAsCode CR Not Ready
Check the CR status and events:
Example output (abbreviated):
TektonInstallerSet Issues
When the TektonInstallerSet is not Ready, read its conditions and the operator's view of the underlying OpenShiftPipelinesAsCode CR. Both are read-only checks; never delete the installer set yourself.
If errors persist, recreate the OpenShiftPipelinesAsCode CR — the operator rebuilds the installer set from scratch.
CR Not Deleting
A deletion that hangs is usually held by the operator finalizer. List the finalizers:
A tekton.dev/operator finalizer means the operator is still cleaning up; wait and retry. An empty output means the CR is free to delete.
Resources Not Removed
When pods or Services linger after deletion: