Setup EventListener

TOC

What This Document Helps You Do

This document helps you configure and deploy an EventListener to receive webhook events from external systems (such as GitHub, GitLab, or custom webhooks) and automatically trigger your Tekton pipelines. You will learn:

  • How to create and configure an EventListener resource
  • How to expose EventListener to external systems (using Ingress, LoadBalancer, or NodePort)
  • How to configure permissions and security settings
  • How to choose the right deployment strategy based on your environment

Prerequisites: You should have basic knowledge of Tekton Pipelines and Kubernetes concepts. If you're new to EventListener concepts, we recommend reading the In-Depth Understanding of EventListener document first.

TIP

For an in-depth understanding of EventListener concepts, architecture, and principles, please refer to the In-Depth Understanding of EventListener document.

Overview

EventListener is a core resource in Tekton Triggers, responsible for receiving and processing external events (such as Webhooks). When an external system triggers an event, the EventListener creates Kubernetes resources (such as PipelineRun) based on the configured triggers.

Key Features

EventListener has the following key features:

  • Event Listening: Provides an HTTP endpoint to receive Webhook events from external systems
  • Event Filtering: Validates and filters received events using interceptors
  • Resource Creation: Automatically creates Kubernetes resources based on trigger definitions
  • Extensibility: Supports custom interceptors and various event sources
  • Security: Built-in multiple security mechanisms such as Webhook validation

Configuration Instructions

Basic Structure

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: eventlistener
spec:
  serviceAccountName: triggers-default-sa
  resources:
    kubernetesResource:
      serviceType: NodePort  # Service type
      servicePort: 80       # Service port
      spec:
        template:
          spec:
            containers:
              - resources:    # Resource limits
                  requests:
                    memory: "64Mi"
                    cpu: "250m"
                  limits:
                    memory: "128Mi"
                    cpu: "500m"
  triggers:                  # Trigger configuration
    - name: trigger-1       # Trigger name
      interceptors:         # Interceptor configuration
        - ref:
            name: "cel"
          params:
            - name: "filter"
              value: "header.match('X-GitHub-Event', 'pull_request')"
      bindings:            # Trigger bindings
        - ref: pipeline-binding
      template:            # Trigger template
        ref: pipeline-template

Main Field Descriptions

spec.resources.kubernetesResource

Used to configure the Kubernetes resources for the EventListener:

  • serviceType: Service type (NodePort/ClusterIP/LoadBalancer)
  • servicePort: Service port
  • spec: Pod template configuration

spec.triggers

Defines a set of trigger configurations:

  • name: Trigger name
  • interceptors: List of interceptor configurations
  • bindings: Trigger binding configurations
  • template: Trigger template configurations

Security Configurations

EventListener supports multiple security configurations:

  1. ServiceAccount:
    • Default ServiceAccount (triggers-default-sa): If spec.serviceAccountName is not specified, EventListener will automatically use the triggers-default-sa ServiceAccount in the namespace. This ServiceAccount is automatically granted the necessary namespace-scoped permissions required for EventListeners to handle triggers within the same namespace. The permissions are automatically bound to the namespace where the ServiceAccount exists.
    • Custom ServiceAccount: When you need to handle triggers across multiple namespaces (e.g., when namespaceSelector is configured), you must specify a custom ServiceAccount with cluster-scoped permissions. Ensure that the specified ServiceAccount is configured with the corresponding permissions (see Permission Guidelines below).
  2. Interceptor Validation: Use CEL interceptors for event validation.
  3. TLS: Supports configuring HTTPS certificates.

Permission Guidelines

Default ServiceAccount vs Custom ServiceAccount

  • Default ServiceAccount (triggers-default-sa): When spec.serviceAccountName is not specified, EventListener automatically uses the triggers-default-sa ServiceAccount in the namespace. This ServiceAccount is automatically granted the necessary namespace-scoped permissions required for EventListeners to handle triggers within the same namespace. The permissions are automatically bound to the namespace where the ServiceAccount exists, so you don't need to manually configure Role or RoleBinding.

  • Custom ServiceAccount with Cluster Permissions: When you configure namespaceSelector to listen to triggers from multiple namespaces (including '*' for all namespaces), you must specify a custom ServiceAccount with cluster-scoped permissions. The triggers-default-sa ServiceAccount only has namespace-scoped permissions and does not have the necessary cluster-scoped permissions to access resources across namespaces.

Required Permissions

To trigger pipeline and tasks properly, the ServiceAccount used by EventListener needs the following permissions:

PermissionResourceDescription
get, list, watchconfigmapsRead ConfigMaps
get, list, watchsecretsRead Secrets
get, list, watchserviceaccountsRead ServiceAccounts
create, get, list, watch, patch, updatedeploymentsManage Deployments
create, get, list, watch, patch, updateservicesManage Services
create, get, list, watch, patch, updatepodsManage Pods
create, get, list, watch, patch, updateeventsManage Events
createpipelinerunsCreate PipelineRuns
createtaskrunsCreate TaskRuns
get, list, watchclustertriggerbindingsRead ClusterTriggerBindings
get, list, watchtriggerbindingsRead TriggerBindings
get, list, watchclusterinterceptorsRead ClusterInterceptors
get, list, watchinterceptorsRead Interceptors
get, list, watchtriggersRead Triggers
get, list, watchtriggertemplatesRead TriggerTemplates
get, list, watcheventlistenersRead EventListeners

Referable ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: eventlistener-role
rules:
- apiGroups: [""]
  resources: ["configmaps", "secrets", "serviceaccounts"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["create", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
  resources: ["services", "pods", "events"]
  verbs: ["create", "get", "list", "watch", "patch", "update"]
- apiGroups: ["tekton.dev"]
  resources: ["pipelineruns", "taskruns"]
  verbs: ["create"]
- apiGroups: ["triggers.tekton.dev"]
  resources:
    - "clustertriggerbindings"
    - "triggerbindings"
    - "clusterinterceptors"
    - "interceptors"
    - "triggers"
    - "triggertemplates"
    - "eventlisteners"
  verbs: ["get", "list", "watch"]

User Guide

Deploying the EventListener needs to be planned according to the scale and the actual network situation of the environment, as described below on how to configure differently based on planning:

EventListener Trigger Configuration Scenarios

EventListener supports three different scenarios for configuring triggers, each suitable for different use cases:

1. Namespace-Level EventListener

A namespace-level EventListener binds to specific triggers within the same namespace by explicitly configuring the triggers field in the EventListener spec. This is the simplest scenario and is suitable for single-namespace deployments.

Use Cases and Recommendations:

  • Use Cases:

    • Independent deployment and management for a single project or team
    • Requires explicit resource isolation and permission control
    • Teams have in-depth understanding of EventListener configuration and need fine-grained control over each trigger
    • Different namespaces require different EventListener configurations or resource limits
    • High requirements for performance and fault isolation
  • Recommended For:

    • Experienced DevOps teams or scenarios requiring high customization
    • When different projects need different EventListener configurations, resource quotas, or security policies
    • Multi-tenant environments requiring complete isolation between tenants
    • When different namespaces need different replica counts, resource limits, or network policies

Example Configuration:

spec:
  # serviceAccountName is optional, defaults to triggers-default-sa
  triggers:
    - name: trigger-1
      bindings:
        - ref: gitlab-push
          kind: ClusterTriggerBinding
      template:
        ref: pipeline-template
    - name: trigger-2
      bindings:
        - ref: github-pullrequest
          kind: ClusterTriggerBinding
      template:
        ref: pipeline-template

2. Project-Level EventListener

A project-level EventListener uses namespaceSelector to bind to triggers across multiple specified namespaces. This is suitable for managing triggers across a set of related namespaces (e.g., a project or team).

Use Cases and Recommendations:

  • Use Cases:

    • Multiple related projects or namespaces need to share the same EventListener
    • Unified event handling at the project group or department level
    • Need to share resources across multiple namespaces without covering the entire cluster
    • Some performance isolation requirements, but can accept resource sharing within the project group
  • Recommended For:

    • Medium-sized teams or project groups that need unified trigger management across multiple related namespaces
    • When multiple projects use similar configurations and resource requirements, reducing operational costs
    • When centralized management of Webhook events for a group of related projects is needed
    • Teams with some understanding of EventListener who can configure and manage cross-namespace permissions

Example Configuration:

spec:
  serviceAccountName: eventlistener  # Custom SA with cluster permissions
  namespaceSelector:
    matchNames:
      - project-a
      - project-b
      - project-c

3. Global-Level EventListener

A global-level EventListener uses namespaceSelector with a wildcard ('*') to bind to triggers from all namespaces in the cluster. This is suitable for centralized event handling across the entire cluster.

Use Cases and Recommendations:

  • Use Cases:

    • Cluster-wide unified event processing and management
    • Users have no strict requirements for performance isolation and can accept resource sharing
    • One-time configuration enables all users to use it out-of-the-box without separate configuration in each namespace
    • Users don't need in-depth knowledge of EventListener, managed uniformly by the platform
    • Small to medium-sized clusters with relatively controllable event volumes
  • Recommended For:

    • Platform scenarios where platform administrators configure and manage uniformly
    • When cluster scale is small and all users need to use the same EventListener configuration
    • Simplifying user workflows and reducing learning costs and configuration complexity
    • Centralized operational management for unified monitoring and troubleshooting
    • Note: In large-scale clusters or high-concurrency scenarios, performance impact and fault isolation should be considered

Example Configuration:

spec:
  serviceAccountName: eventlistener  # Custom SA with cluster permissions
  namespaceSelector:
    matchNames:
      - '*'  # Wildcard for all namespaces

Comparison Table:

ScenarioNamespace ScopeServiceAccountConfiguration ComplexityUse Case
Namespace-LevelSingle namespacetriggers-default-sa (auto)SimpleSingle project/team
Project-LevelMultiple specified namespacesCustom SA (cluster permissions)ModerateMultiple related projects
Global-LevelAll namespaces (*)Custom SA (cluster permissions)ComplexCluster-wide management

Scale

In different planning scenarios, different configurations can be used to meet varying requirements.

ScaleTrigger CountResource Settings
Small Scale2 (triggers) * 100 (namespaces) * 10 (pipelines) = 2,000 triggers/clusterModerate, more than two replicas
Medium Scale2 (triggers) * 1,000 (namespaces) * 10 (pipelines) = 20,000 triggers/clusterModerate, more than two replicas, deploy different EventListeners by namespace

Network Configuration

Depending on the priority of the environment and the available network resources, different network configurations can be chosen.

Network ConfigurationProtocolDescription
Official Domain and CertificatehttpsSet up TLS certificates via ClusterIP + Ingress
Custom Domain and CertificatehttpsUse self-signed certificates for TLS. Note: There is a risk in the tool-side configuration, not all tools/platforms support skipping or ignoring TLS validation.
NodePorthttpSet accessible addresses via NodePort + Cluster / Node IP
TIP

Choosing the Right Network Configuration

  • Production Environments: Use LoadBalancer or Ingress with HTTPS for security and reliability
  • Development/Testing: NodePort is sufficient and easier to set up
  • Automatic Management: If you want the system to automatically create Ingress resources, use the automatic exposure feature configured through TektonConfig
  • Manual Control: If you prefer manual control over Ingress resources, create them manually as shown in the examples below

NodePort Configuration Example

NodePort is suitable for development, testing, or environments where you don't have a LoadBalancer or Ingress controller configured. This method exposes the EventListener service on a high port (30000-32767) on each node in the cluster.

Prerequisites

  • Kubernetes cluster with nodes accessible from external systems
  • You should know at least one node's external IP address
  • Firewall rules should allow traffic to the NodePort range (30000-32767)

Configuration Example

Create EventListener with NodePort Service

Save the following YAML as eventlistener-nodeport.yaml:

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: el-demo
  namespace: tekton-triggers-demo
spec:
  # serviceAccountName is optional, defaults to triggers-default-sa
  serviceAccountName: triggers-default-sa
  resources:
    kubernetesResource:
      serviceType: NodePort  # Use NodePort service type
      servicePort: 80        # Container port
  triggers:
    - name: hello-trigger
      bindings:
        - ref: hello-binding
      template:
        ref: hello-template
kubectl apply -f eventlistener-nodeport.yaml

Get NodePort Webhook Address

After the EventListener is created, you need to get the NodePort and node IP to construct the webhook URL:

# 1. Get the NodePort assigned to the EventListener service
# Replace 'el-el-demo' with your actual service name (format: el-<eventlistener-name>)
# Replace 'tekton-triggers-demo' with your namespace
export NODEPORT=$(kubectl get svc el-el-demo -n tekton-triggers-demo -o jsonpath='{.spec.ports[0].nodePort}')
echo "NodePort: $NODEPORT"

# 2. Get a node's external IP address
# This gets the first node's external IP, you can also use internal IP if nodes are not directly accessible
export NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')

# If no ExternalIP, try InternalIP
if [ -z "$NODE_IP" ]; then
  export NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
fi
echo "Node IP: $NODE_IP"

# 3. Construct the webhook URL
export WEBHOOK_URL="http://${NODE_IP}:${NODEPORT}"
echo "Webhook URL: $WEBHOOK_URL"

Alternative: Get all node IPs and NodePort

# Get NodePort
NODEPORT=$(kubectl get svc el-el-demo -n tekton-triggers-demo -o jsonpath='{.spec.ports[0].nodePort}')

# Get all node external IPs
kubectl get nodes -o jsonpath='{range .items[*]}{.status.addresses[?(@.type=="ExternalIP")].address}{"\n"}{end}'

# Or get internal IPs if external IPs are not available
kubectl get nodes -o jsonpath='{range .items[*]}{.status.addresses[?(@.type=="InternalIP")].address}{"\n"}{end}'

The webhook URL format is: http://<node-ip>:<nodeport>

Test NodePort Webhook

# Test the webhook endpoint
curl -v -X POST -H 'Content-Type: application/json' \
  -d '{"message":"Hello from NodePort!"}' \
  $WEBHOOK_URL

Verify EventListener Status

# Check EventListener is ready
kubectl get eventlistener el-demo -n tekton-triggers-demo

# Check the service
kubectl get svc el-el-demo -n tekton-triggers-demo

# View EventListener Pod logs
kubectl logs -n tekton-triggers-demo -l app=el-el-demo

Important Notes for NodePort

  • Security: NodePort uses HTTP (not HTTPS), so it's not suitable for production environments where security is critical
  • Accessibility: The node IP must be accessible from external systems that will send webhooks
  • High Availability: If a node goes down, you'll need to use a different node's IP. Consider using a load balancer in front of multiple nodes
  • Port Range: NodePort uses ports in the range 30000-32767. Make sure your firewall allows traffic to these ports
  • Multiple Nodes: You can use any node's IP address, as the NodePort is available on all nodes

When to Use NodePort

  • Development and Testing: Quick setup for local development or testing
  • Internal Networks: When webhook senders are on the same internal network as your cluster nodes
  • No LoadBalancer Available: When your cluster doesn't have a LoadBalancer service or Ingress controller
  • Cost Considerations: When you want to avoid LoadBalancer costs
WARNING

Production Recommendation: For production environments, it's strongly recommended to use LoadBalancer or Ingress with HTTPS instead of NodePort, as NodePort lacks encryption and proper domain-based routing.

Transitioning from NodePort to Ingress

If you started with NodePort for testing and now want to move to a production setup with Ingress:

  1. Update EventListener Service Type: Change serviceType from NodePort to ClusterIP in your EventListener spec
  2. Configure Automatic Exposure (Recommended): Use the automatic exposure feature to automatically create Ingress resources
  3. Or Create Ingress Manually: Follow the example below to create Ingress resources manually

The following example shows how to set up EventListener with Ingress for production use.

Small Scale + HTTPS + ALB Ingress Configuration Example

Prerequisites

Before configuring the EventListener with Ingress, ensure the following prerequisites are met:

  1. LoadBalancer/Gateway Configuration:

    • A LoadBalancer or Gateway (such as ALB) should be deployed and configured in your cluster
    • The LoadBalancer should be accessible from external systems that will send webhooks
    • For more information on configuring a Load Balancer, refer to Configure a Load Balancer.
  2. Domain and Certificate Setup:

    • The domain name is configured correctly and points to your LoadBalancer
    • TLS certificates are in place (either manually created or via cert-manager)
    • DNS records are properly configured
  3. Ingress Controller:

    • An Ingress controller (ALB in this example) is deployed and configured properly
    • The Ingress controller is ready to handle Ingress resources
    • For more information on creating Ingresses, refer to Creating Ingresses.
INFO

Note: If you're using the automatic exposure feature (configured through TektonConfig), you typically don't need to manually create Ingress resources. The system will automatically create them based on your export rules. This example shows manual Ingress creation for cases where you prefer manual control or are not using the automatic exposure feature.

Configuration Example

Create Namespace (Optional)

Ensure there is a Namespace for easy management of EventListener and other permissions; here we use tekton-webhooks as an example.

kubectl create namespace tekton-webhooks

Create ClusterRole and ServiceAccount and Set Permissions

  • Default ServiceAccount (triggers-default-sa): When spec.serviceAccountName is not specified, EventListener automatically uses the triggers-default-sa ServiceAccount in the namespace. This ServiceAccount is automatically granted the necessary namespace-scoped permissions required for EventListeners to handle triggers within the same namespace. The permissions are automatically bound to the namespace where the ServiceAccount exists, so you don't need to manually configure Role or RoleBinding.

  • Custom ServiceAccount with Cluster Permissions: When you configure namespaceSelector to listen to triggers from multiple namespaces (including '*' for all namespaces), you must specify a custom ServiceAccount with cluster-scoped permissions. The triggers-default-sa ServiceAccount only has namespace-scoped permissions and does not have the necessary cluster-scoped permissions to access resources across namespaces.

WARNING

Required for namespaceSelector: The following steps are only required when using namespaceSelector to listen to triggers across multiple namespaces. If your EventListener only handles triggers within the same namespace, you can skip these steps and use the default triggers-default-sa ServiceAccount, which will automatically have the required permissions.

The following YAML is for eventlistener-role.yaml.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: eventlistener-role
rules:
- apiGroups: [""]
  resources: ["configmaps", "secrets", "serviceaccounts"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["create", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
  resources: ["services", "pods", "events"]
  verbs: ["create", "get", "list", "watch", "patch", "update"]
- apiGroups: ["tekton.dev"]
  resources: ["pipelineruns", "taskruns"]
  verbs: ["create"]
- apiGroups: ["triggers.tekton.dev"]
  resources:
    - "clustertriggerbindings"
    - "triggerbindings"
    - "clusterinterceptors"
    - "interceptors"
    - "triggers"
    - "triggertemplates"
    - "eventlisteners"
  verbs: ["get", "list", "watch"]
kubectl apply -f eventlistener-role.yaml

Create a binding using the ClusterRole and ServiceAccount above.

kubectl -n tekton-webhooks create serviceaccount eventlistener
kubectl create clusterrolebinding tekton-webhooks:eventlistener:eventlistener-role --clusterrole=eventlistener-role --serviceaccount=tekton-webhooks:eventlistener

Create EventListener

Save the following YAML as eventlistener.yaml.

INFO

ServiceAccount Selection:

  • If you only need to handle triggers within the same namespace, you can omit spec.serviceAccountName to use the default triggers-default-sa ServiceAccount, which will automatically have the required namespace-scoped permissions.
  • Since this example uses namespaceSelector with '*' to listen to triggers from all namespaces, a custom ServiceAccount with cluster-scoped permissions is required. Therefore, serviceAccountName: "eventlistener" is specified here.
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: eventlistener
  namespace: tekton-webhooks
  labels:
    el.tekton.dev/namespaces: all
    el.tekton.dev/size: small
spec:
  serviceAccountName: "eventlistener" # Required: Custom SA with cluster permissions needed for namespaceSelector
  # Listen to triggers from all namespaces
  namespaceSelector:
    matchNames:
    - '*'
  # Declare Kubernetes resource, default is Deployment
  resources:
    kubernetesResource:
      serviceType: ClusterIP # ClusterIP service type
      servicePort: 80  # Service port (container port)
      replicas: 2 # Replicas
      spec: # Deployment spec
        template:
          metadata:
            labels:
              el.tekton.dev/namespaces: all
              el.tekton.dev/size: small
          spec: {}
kubectl apply -f eventlistener.yaml

Create Ingress and TLS Secrets

Info

You need to set the <host> with the corresponding domain name and certificate information.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: eventlistener-ingress
  annotations:
    ### Note ALB does not require setting ingressClassName
    ## kubernetes.io/ingress.class: "nginx" ## Follow changes to the ingress class, and remember to add corresponding ingress annotations.
    ### The following configuration should be customized based on requirements:
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ### Note ALB does not require setting ingressClassName
  ## ingressClassName: alb ## Change according to the required ingress class
  tls:
  - hosts:
    - <host> # Domain name setting. Ensure DNS resolution is configured (or add to /etc/hosts for testing)
    secretName: <tls secret> # Name of the secret containing TLS certificate
  rules:
  - host: <host> # Domain name setting. Ensure DNS resolution is configured (or add to /etc/hosts for testing)
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: el-eventlistener 
            port:
              number: 80
WARNING

DNS Configuration: When configuring the host field with a domain name, ensure that:

  • DNS records are properly configured to resolve the domain to your Ingress controller's IP address
  • For local testing, you can add the domain to /etc/hosts (Linux/Mac) or C:\Windows\System32\drivers\etc\hosts (Windows)
  • The domain must be resolvable from the systems that will send webhooks (GitHub, GitLab, etc.)

Verify Domain Accessibility

After configuring the domain and Ingress, ensure that your webhook domain is accessible:

# Test domain accessibility (replace <host> with your actual domain)
curl -kI https://<host>

# Or test the specific webhook endpoint
curl -kI https://<host>/triggers/<eventlistener-namespace>/<eventlistener-name>
WARNING

Important: Ensure that your webhook domain based on the LoadBalancer is accessible from external systems (such as GitHub, GitLab, or other webhook senders). If the domain is not accessible, webhooks will fail. Check:

  • DNS records are correctly configured and propagated
  • LoadBalancer is properly configured and has an external IP/domain
  • Firewall rules allow traffic to the LoadBalancer
  • TLS certificates are valid (if using HTTPS)

Validate Webhook Configuration

You can test whether the configuration is normal using the following curl.

curl -k -X POST -H 'Content-Type: application/json' -d '{"hello": "world"}' https://<host>/triggers/<eventlistener-namespace>/<eventlistener-name>

## The following is the expected return format:
$> {
  "eventListener": "eventlistener",
  "namespace": "tekton-webhooks",
  "eventListenerUID": "8a7edab2-b426-453a-9f92-xxxxxx",
  "eventID": "aefd3b5b-2b19-4a14-b411-xxxxxxx"
}

Best Practices

  1. Resource Limits:

    • Set appropriate resource requests and limits for EventListener Pods.
    • Adjust the number of replicas based on actual load.
  2. Security:

    • Use HTTPS and Webhook Secrets.
    • Configure the least privilege ServiceAccount.
    • Validate all incoming events using interceptors.
  3. Availability:

    • Expose services using LoadBalancer or Ingress.
    • Configure appropriate health checks.
    • Implement high-availability deployments.
  4. Monitoring:

    • Monitor EventListener logs.
    • Set appropriate alert mechanisms.
    • Track event processing performance.

Frequently Asked Questions

  1. Events Not Triggering Pipeline

    • Check interceptor configurations.
    • Validate Webhook configuration.
    • Review EventListener logs.
  2. Permission Issues

    • Confirm ServiceAccount permissions.
    • If using the default triggers-default-sa ServiceAccount, verify that it exists in the namespace and has been automatically granted the required permissions.
    • If using a custom ServiceAccount, check Role and RoleBinding configurations.
    • Verify namespace access permissions.
    • If using namespaceSelector to listen across namespaces, ensure you're using a custom ServiceAccount with cluster-scoped permissions. The triggers-default-sa ServiceAccount only has namespace-scoped permissions.
  3. Performance Issues

    • Adjust resource limits.
    • Optimize interceptor configurations.
    • Consider horizontal scaling.