Configure EventListener automatic exposure rules
TOC
What This Document Helps You DoFeature overviewConfiguration entry pointField referenceField Mapping to Ingress ResourcesRequest flow diagramConfiguration examplesExample 1: wildcard host with custom prefixExample 2: shared hostname and prefixExample 3: environment-specific rulesExample 4: team-scoped publishing with default prefixExample 5: multiple external endpoints and adjusted prefixExample 6: Configuring TLS/HTTPSOption A: Manual TLS with pre-created SecretOption B: Using cert-manager for automatic certificate managementOption C: Combined TLS and annotationsGateway API (HTTPRoute) modeHTTPS in Gateway API modeConfiguration workflowVerification and troubleshootingVerify ConfigMap contentVerify Ingress objectsCheck EventListener addressesInspect Trigger annotationsTroubleshooting tipsWhat This Document Helps You Do
This document helps you configure automatic external exposure of EventListeners through the automatic exposure feature. You will learn:
- How to configure export rules to automatically expose EventListeners via Ingress or the Gateway API (HTTPRoute)
- How to set up webhook URLs that will be displayed in the UI
- How to configure different exposure strategies for different namespaces or environments
- How to verify that EventListeners are properly exposed
When to use this: Use this feature when you want the system to automatically create Ingress resources for your EventListeners, eliminating the need to manually create and manage Ingress resources. This is especially useful in production environments where you need consistent webhook URL patterns.
Prerequisites: You should have cluster administrator privileges to configure TektonConfig resources, and you should have a basic understanding of Kubernetes Ingress and networking concepts.
Feature overview
The automatic exposure controller (internally named trigger-wrapper) reads the trigger-wrapper-config ConfigMap in the Tekton system namespace (default: tekton-pipelines). The export-rules defined in that ConfigMap determine which EventListeners should be exposed externally (Service, Ingress, etc.) and populate the EventListener/Trigger status with the generated endpoints.
Technical Note: The internal component name is trigger-wrapper, but you don't need to remember this. You only need to configure the export rules through TektonConfig, and the system will handle the rest automatically.
Configuration entry point
On Alauda Tekton, it is recommended to manage this configuration through the TektonConfig custom resource. Embed the rule definitions under spec.pipeline.options.configMaps.trigger-wrapper-config.data.config, for example:
The Operator synchronises this spec into the trigger-wrapper-config ConfigMap inside Tekton system namespace (default: tekton-pipelines). Once updated, the automatic exposure controller refreshes its cache and reconciles resources according to the new rules.
Note: The ConfigMap name trigger-wrapper-config is an internal technical name. You manage the configuration through TektonConfig as shown above, and don't need to directly interact with the ConfigMap.
Field reference
Each entry in export-rules represents a publishing strategy. Important fields:
-
name – rule name, also used when generating Service/Ingress names. Avoid name collisions: the shared Ingress name is the rule name normalized to a valid DNS-1123 subdomain (lower-cased, characters outside
[a-z0-9-]replaced with-, repeated-collapsed). This normalization is lossy, so rule names that differ only by such characters resolve to the same Ingress and overwrite each other — for examplea_b,a.b, andA-Ball becomea-b. Give each rule a name that is already a distinct, valid DNS-1123 subdomain (lower-case letters, digits, and-); do not rely on_,., or letter case to distinguish two rules. -
ingressClass (optional) – the Ingress controller to use, e.g.
nginx,traefik. -
host (optional) – hostname matched by the Ingress. Leave empty to accept all hosts. Important: When configuring a domain name, ensure DNS resolution is properly configured (or add to
/etc/hostsfor local testing). The domain must be resolvable from systems that will send webhooks. -
externalHosts (optional) – What it does: Defines the webhook URLs that will be displayed to users in the UI and populated in EventListener's
status.addressesfield.Think of it as: The "public address" that external systems (like GitHub, GitLab) will use to send webhooks to your EventListener.
How it works:
- The controller combines each
externalHostsURL with the generated path:${externalHost}/<urlPathPrefix>/<eventlistener-namespace>/<eventlistener-name> - For example:
externalHosts: ["https://webhooks.example.com"]+urlPathPrefix: /triggers→ Final URL:https://webhooks.example.com/triggers/my-namespace/my-listener - These URLs appear in EventListener's
status.addressesfield, making it easy to copy and paste into GitHub/GitLab webhook configuration
Common scenarios:
What happens if you fill it wrong?
- ✅ Ingress will still work correctly (this field doesn't affect Ingress creation)
- ❌ Users will see incorrect webhook URLs in the UI
- ❌ Copying the URL from
status.addressesto GitHub/GitLab will fail - 🔧 Fix: Update the
externalHostsvalue in TektonConfig and the controller will refresh EventListener status
Key differences from
hostfield:Rule of thumb:
- If you can access your webhook at
https://webhooks.example.com:8443/triggers/ns/el, then fill:externalHosts: ["https://webhooks.example.com:8443"] - The controller will append the path automatically
Important notes:
- Always include the protocol (
http://orhttps://) - Include custom ports if your LoadBalancer uses non-standard ports
- Don't include the path in
urlPathPrefix(like/triggers) - the controller appends it to the end of theexternalHostsautomatically - If unsure, leave it empty and check the actual accessible URL first, then update the configuration
- The controller combines each
-
urlPathPrefix (optional) – path prefix; defaults to
/triggers. The final path rendered in the Ingress is${urlPathPrefix}/${eventlistener-namespace}/${eventlistener-name}. Always start with/and avoid trailing slashes. -
namespaceSelector.matchNames (optional) – namespaces allowed by this rule. Use
"*"to target all namespaces. -
labelSelector (optional) – Kubernetes LabelSelector used to filter EventListeners.
-
tls (optional) – TLS configuration for Ingress. Each entry specifies
hosts(list of hostnames) andsecretName(name of the TLS Secret containing the certificate). -
annotations (optional) – additional annotations for Ingress. Useful for cert-manager, nginx-specific settings, etc. Only your annotations are applied; the controller no longer injects any controller-specific annotation (such as
nginx.ingress.kubernetes.io/rewrite-target). The generated Ingress path usespathType: Exactand no rewrite, so exposure works with any Ingress controller (nginx, traefik, ALB, …), not just nginx. -
type (optional) – exposure resource:
Ingress(default) orHTTPRoute(Gateway API). Empty defaults toIngressfor backward compatibility. See Gateway API (HTTPRoute) mode. -
gateway (optional) – required when
type: HTTPRoute. References the parent Gateway the HTTPRoute attaches to:name(required),namespace(defaults to the Tekton system namespace), optionalsectionNameandport.
Namespace matching currently supports
matchNamesonly. If you need label-based namespace selection, enumerate the namespaces explicitly.
Field Mapping to Ingress Resources
The following table shows how export rule fields map to the generated Ingress resource:
Example Mapping:
Given this export rule:
DNS Configuration: When using a domain name in the host field, ensure DNS records are configured to resolve the domain to your Ingress controller's IP. For local testing, you can add entries to /etc/hosts (Linux/Mac) or C:\Windows\System32\drivers\etc\hosts (Windows).
The generated Ingress will have:
metadata.name:test-webhooksspec.ingressClassName:nginxspec.rules[0].host:webhooks.example.comspec.rules[0].http.paths[].path:/triggers/${namespace}/${eventlistener-name}(for each matching EventListener)spec.tls[0].hosts:["webhooks.example.com"]spec.tls[0].secretName:webhooks-tls-secretmetadata.annotations: Includescert-manager.io/cluster-issuerand controller-managed annotations
Request flow diagram
externalHoststells external clients which URL to call. The Ingress matches requests byhostand the exact path${urlPathPrefix}/${namespace}/${eventlistener}, and forwards to an ExternalName Service in the Tekton system namespace that resolves to the EventListener sink Service. Because the sink accepts any path, the backend receives the full path unchanged (no rewrite).
Configuration examples
Example 1: wildcard host with custom prefix
Result: the Ingress exposes /hooks/default/${namespace}/${eventlistener}. Because host is empty, any hostname will be accepted—ideal when an external gateway assigns the public domain.
Example 2: shared hostname and prefix
Result: every EventListener appears at https://webhooks.example.com/triggers/${namespace}/${eventlistener}; the backend sees the same path.
Example 3: environment-specific rules
Result:
- GitLab webhooks:
https://gitlab-staging.example.com/staging/gitlab/${namespace}/${eventlistener} - GitHub webhooks:
https://github-prod.example.com/prod/github/${namespace}/${eventlistener}
Example 4: team-scoped publishing with default prefix
Result: only EventListeners in team-a are exposed, at /triggers/team-a/${eventlistener}.
Example 5: multiple external endpoints and adjusted prefix
Result:
- Ingress serves
webhook.internal.local/internal/hooks/${namespace}/${eventlistener}internally. - Externally you can publish
https://webhooks.example.com/hooks/internal/hooks/${namespace}/${eventlistener}andhttps://backup.example.com/api/hooks/internal/hooks/${namespace}/${eventlistener}. - The backend Service always receives
/internal/hooks/${namespace}/${eventlistener}.
Example 6: Configuring TLS/HTTPS
Option A: Manual TLS with pre-created Secret
-
Create a TLS Secret containing your certificate:
-
Configure your export rule with TLS:
The controller will automatically configure the Ingress with TLS using the specified Secret.
Option B: Using cert-manager for automatic certificate management
Configure your export rule with cert-manager annotations:
cert-manager will automatically:
- Create a Certificate resource
- Obtain a certificate from Let's Encrypt (or your configured issuer)
- Create a TLS Secret
- Update the Ingress with TLS configuration
Option C: Combined TLS and annotations
You can combine manual TLS configuration with additional annotations:
Note: When both
tlsand cert-manager annotations are configured, thetlsconfiguration takes precedence. For automatic certificate management, use cert-manager annotations withouttlsconfiguration.
Gateway API (HTTPRoute) mode
In addition to Ingress, an export rule can expose EventListeners through the
Gateway API by setting type: HTTPRoute.
This is the recommended mode for clusters that have no Ingress controller (or no
usable ingressClass) but do run a Gateway implementation such as Envoy Gateway.
The existing Ingress behaviour is unchanged, and Gateway mode also keeps
status.addresses populated so the UI and Git webhook registration keep working.
Prerequisites:
- The Gateway API CRDs and a Gateway implementation (e.g. Envoy Gateway) are
installed. If the CRDs are absent, the controller stays in Ingress-only mode
and records a warning event on any
type: HTTPRouterule. - A parent
Gatewayalready exists. Its listenerallowedRoutes.namespacesmust permit the Tekton system namespace (defaulttekton-pipelines), because the generated HTTPRoute is created there. The controller does not modify the Gateway; it only reads the HTTPRoute'sAcceptedcondition. externalHostsis required in this mode — it is the source ofstatus.addresses.
Example:
What the controller creates:
- One
HTTPRouteper matching EventListener in the system namespace, with a singlepath.type: Exactmatch (${urlPathPrefix}/${namespace}/${eventlistener}), aparentRefsentry pointing at your Gateway, and a cross-namespacebackendRefsentry that targets the EventListener sink Service (el-<name>, port 8080) directly. NoURLRewritefilter is used — the sink accepts the full path. - One
ReferenceGrantper EventListener in the EventListener's namespace, scoped byto.nameto that single sink Service, so the system-namespace HTTPRoute may reference it across the namespace boundary (least privilege).
Mode-specific field behaviour: ingressClass, tls and annotations apply
to Ingress mode only and are ignored when type: HTTPRoute. For HTTPS in Gateway
mode, terminate TLS on the Gateway listener instead — see
HTTPS in Gateway API mode below.
Verification:
status.addresses (and the Trigger annotation endpoints) are published only once
the HTTPRoute is Accepted by its Gateway. If an address does not appear, check
kubectl get httproute <name> -n tekton-pipelines -o yaml for
status.parents[].conditions — an Accepted: False or ResolvedRefs: False
condition usually means the Gateway listener does not allow the system namespace,
or the sink Service is not reachable.
HTTPS in Gateway API mode
In the Gateway API, TLS is terminated on the Gateway listener, not on the
HTTPRoute — trigger-wrapper never manages certificates in this mode (the tls
field applies to Ingress mode only). To serve an EventListener over HTTPS, attach
the export rule to a Gateway that has an HTTPS listener whose tls.certificateRefs
points at your certificate Secret, and pin the rule to that listener with
gateway.sectionName (or gateway.port).
1. A Gateway with an HTTPS listener (managed by the platform / cluster admin):
The certificate Secret (webhooks-tls above, holding tls.crt / tls.key)
and the Gateway are cluster/platform-managed. allowedRoutes.namespaces.from
must permit the Tekton system namespace, because the generated HTTPRoute is
created there.
2. The export rule, pinned to that HTTPS listener via gateway.sectionName:
Notes:
- Set the rule's
hostto the listenerhostnameso the HTTPRoute hostname matches the TLS SNI, and use thehttps://scheme inexternalHostsso the publishedstatus.addressesadvertise HTTPS. gateway.sectionNamepins the HTTPRoute to a named listener; usegateway.portinstead (or as well) when listeners are distinguished by port. Omitting both lets the Gateway attach the route to any compatible listener.- trigger-wrapper only creates the HTTPRoute (and its
ReferenceGrant); it does not create or modify the Gateway, the listener, or the certificateSecret.
Configuration workflow
- Edit the
TektonConfigresource (see Configuration entry point). - Apply the changes:
kubectl apply -f tektonconfig.yaml. - Wait for the Operator to propagate the ConfigMap; the automatic exposure controller will then reconcile new resources automatically.
Verification and troubleshooting
Verify ConfigMap content
Check that the ConfigMap contains the expected configuration:
Expected output (normal):
What to check:
- The ConfigMap exists and contains the
configkey - The
export-rulesarray matches your TektonConfig specification - YAML syntax is valid (no parsing errors)
Verify Ingress objects
Check that Ingress resources are created for matching export rules:
Important: One shared Ingress is created per export rule in the system namespace. Its name is the rule name, and it carries one pathType: Exact path per matching EventListener (${urlPathPrefix}/${namespace}/${eventlistener}). There is no per-EventListener Ingress in the EventListener's own namespace.
Expected output (normal):
What to check:
- A shared Ingress exists for each export rule that matches at least one EventListener
- The
HOSTSfield matches thehostspecified in the export rule (empty/*means all hosts) - The Ingress has an
ADDRESSassigned (may take a few minutes) - Inspect
kubectl get ingress <rule-name> -n tekton-pipelines -o yamland confirm each matching EventListener has apathType: Exactpath - If no Ingress appears, verify the namespace matches
matchNamesand EventListener labels matchlabelSelector
Check EventListener addresses
Verify that EventListener status contains the generated webhook addresses:
Expected output (normal):
What to check:
- The
addressesarray contains URLs matching yourexternalHostsconfiguration - URLs follow the pattern:
<externalHost>/<urlPathPrefix>/<eventlistener-namespace>/<eventlistener-name> - If
addressesis empty or missing, the EventListener may not match any export rule
Inspect Trigger annotations
Check the export metadata stored in Trigger annotations:
Expected output (normal):
What to check:
- The annotation contains an array of EventListener information
- Each entry includes
name,namespace,endpoints, andrelevancefields - The
endpointsarray matches the EventListener'sstatus.addresses - The
relevance.scoreindicates how well the EventListener matches the Trigger (higher is better) - If the annotation is missing, the Trigger may not have found any matching EventListeners
Troubleshooting tips
-
If a rule does not apply:
- Verify that the namespace is listed in
matchNames(or use"*"for all namespaces) - Check that EventListener labels satisfy the
labelSelectorrequirements - Ensure the EventListener is in
Readystate
- Verify that the namespace is listed in
-
Misconfigured label selectors:
- Appear in controller logs as parsing errors
- Check controller logs:
kubectl logs -n tekton-pipelines -l app=tektoncd-enhancement-controller
-
Removing a rule (or switching its
type):- Cascades deletion of the generated resources for that rule (Ingress + ExternalName Service, or HTTPRoute + ReferenceGrant)
- Switching a rule between
IngressandHTTPRouteprunes the old mode's resources and creates the new ones - Setting
export-rulesto an empty array disables all external exposure - EventListener
status.addresseswill be cleared when no rules match
-
Using IP addresses instead of domain names:
-
Problem: Kubernetes Ingress resources do not support IP addresses as
hostvalues. If you configurehostwith an IP address (e.g.,host: 192.168.1.100), the Ingress will fail to be created or will not work correctly. -
Solution 1: Leave
hostempty or set it to"*"to accept all hosts. The Ingress will match requests regardless of the host header: -
Solution 2: Configure a domain name that resolves to your IP address, then use that domain in the
hostfield:-
Set up DNS resolution: Add an A record pointing your domain to the IP address (e.g.,
webhooks.example.com→192.168.1.100) -
Configure the export rule with the domain name:
-
-
Note:
externalHostscan contain IP addresses or URLs, as it's only used to populate EventListenerstatus.addressesand doesn't affect Ingress creation. However, the Ingress itself must use a valid hostname (or be empty) in thehostfield.
-
By maintaining the ConfigMap through TektonConfig, you can flexibly control how Tekton EventListeners are exposed to external systems. Keep an eye on controller logs during updates to confirm that reconciliations complete successfully.