← Back to DevBytes

KEDA Event-Driven Autoscaling: Complete Implementation Guide

Introduction to KEDA and Event-Driven Autoscaling

What is KEDA?

KEDA (Kubernetes Event-Driven Autoscaling) is a Cloud Native Computing Foundation (CNCF) graduated project that extends Kubernetes with event-driven autoscaling capabilities. While the native Horizontal Pod Autoscaler (HPA) scales workloads based on resource metrics like CPU and memory, KEDA allows you to scale based on the number of events waiting in a queue, metrics from external systems, or custom application metrics. It acts as a thin layer on top of HPA, feeding external metrics into Kubernetes so that scaling decisions become event-aware.

KEDA supports a rich ecosystem of scalers — ready-to-use connectors for Apache Kafka, RabbitMQ, Azure Event Hubs, AWS SQS, GCP Pub/Sub, Prometheus, Redis, and many more. With KEDA, you can scale deployments down to zero when no events are present, drastically reducing infrastructure costs for event-driven workloads.

Why Event-Driven Autoscaling Matters

Traditional resource-based autoscaling often fails to capture the true demand of event-driven applications. A consumer processing messages from a queue might have low CPU usage but a growing backlog of unprocessed events. Relying only on CPU or memory metrics can lead to delayed scaling, dropped messages, and poor user experience.

KEDA solves this by:

How KEDA Works

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

KEDA consists of two main components:

When you create a ScaledObject, KEDA starts polling the event source defined in the trigger. It translates the event count (e.g., queue length) into a target metric value and feeds it to the HPA. The HPA then adjusts the replica count of the target deployment or statefulset accordingly. The whole process is transparent — your pods don't need any KEDA-specific libraries.

Key Concepts: ScaledObject and Triggers

ScaledObject is the main custom resource that defines which workload to scale and how. It references a target deployment (or statefulset) and contains a list of triggers. Each trigger specifies a scaler type (e.g., rabbitmq, kafka) and its metadata (connection details, thresholds, etc.).

Triggers can be combined; when multiple triggers are defined, KEDA scales based on the maximum calculated replica count among them, ensuring capacity for the most demanding source.

For job-based workloads, KEDA provides ScaledJob, which creates one job per event or per batch of events, ideal for fire-and-forget processing.

Step-by-Step Implementation Guide

Prerequisites

Installing KEDA

You can install KEDA using Helm or via static manifests. The Helm approach is simpler and keeps you updated.

Add the KEDA Helm repository and install:

helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace

Verify the installation:

kubectl get pods -n keda
# You should see the operator and metrics-server pods running

For a non-Helm installation, apply the latest release YAML from the KEDA GitHub releases page:

kubectl apply -f https://github.com/kedacore/keda/releases/latest/download/keda.yaml

Deploying a Sample Application

We'll deploy a simple message consumer that reads from a RabbitMQ queue. The deployment starts with zero replicas; KEDA will scale it based on the queue length.

First, create a deployment manifest (consumer-deploy.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: orders-consumer
  labels:
    app: orders-consumer
spec:
  replicas: 0   # start scaled down to zero
  selector:
    matchLabels:
      app: orders-consumer
  template:
    metadata:
      labels:
        app: orders-consumer
    spec:
      containers:
      - name: consumer
        image: ghcr.io/kedacore/sample-rabbitmq-consumer:latest
        env:
        - name: RABBITMQ_HOST
          value: "rabbitmq-service.default.svc.cluster.local"
        - name: QUEUE_NAME
          value: "orders"
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 200m
            memory: 256Mi

Apply it:

kubectl apply -f consumer-deploy.yaml

Ensure the deployment exists with zero replicas:

kubectl get deployment orders-consumer
# READY 0/0

Defining a ScaledObject for Event-Driven Scaling

Now we create a ScaledObject that tells KEDA to monitor the "orders" queue and scale the deployment accordingly.

Create scaled-object.yaml:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: orders-scaler
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: orders-consumer
  minReplicaCount: 0          # allow scaling down to zero
  maxReplicaCount: 20         # upper bound
  cooldownPeriod: 300         # seconds to wait before scaling down after spike
  pollingInterval: 30         # check queue every 30 seconds
  triggers:
  - type: rabbitmq
    metadata:
      host: "amqp://guest:guest@rabbitmq-service.default.svc.cluster.local:5672/"
      queueName: "orders"
      mode: "QueueLength"     # number of messages
      value: "10"             # target queue length per pod
      protocol: "amqp"
      vhost: "/"

Apply it:

kubectl apply -f scaled-object.yaml

KEDA will now poll the RabbitMQ queue every 30 seconds. When the queue length exceeds the threshold (10 messages per pod), it will feed a desired replica count to the HPA, scaling up the deployment. As the queue drains, the replica count reduces, eventually reaching zero if no messages remain.

Testing the Autoscaling Behavior

To see scaling in action, publish messages to the "orders" queue. You can use the RabbitMQ management interface or a simple producer pod.

For example, run a temporary producer pod that sends 100 messages:

kubectl run orders-producer --image=ghcr.io/kedacore/sample-rabbitmq-producer:latest \
  --env="RABBITMQ_HOST=amqp://guest:guest@rabbitmq-service.default.svc.cluster.local:5672/" \
  --env="QUEUE_NAME=orders" --env="MESSAGE_COUNT=100" --rm -it --restart=Never

Watch the deployment scale up:

kubectl get deployment orders-consumer -w
# Observe replicas increasing from 0

Inspect the ScaledObject status:

kubectl describe scaledobject orders-scaler
# Shows trigger status, last poll time, and HPA reference

Once the queue empties, after the cooldown period (300 seconds), the deployment will scale back down to zero.

Using Other Popular Scalers

KEDA's power lies in its scaler ecosystem. Below are examples for Apache Kafka and Prometheus. The structure remains the same — only the trigger type and metadata change.

Apache Kafka Scaler

Scale based on consumer group lag for a specific topic:

triggers:
- type: kafka
  metadata:
    bootstrapServers: "kafka-broker1:9092,kafka-broker2:9092"
    consumerGroup: "orders-group"
    topic: "orders"
    lagThreshold: "5"          # desired max lag per pod
    activationLagThreshold: "0" # optional threshold to activate from zero

Prometheus Scaler

Scale based on any Prometheus metric, e.g., HTTP request rate:

triggers:
- type: prometheus
  metadata:
    serverAddress: http://prometheus-server.monitoring.svc.cluster.local:9090
    metricName: http_requests_total
    threshold: "100"
    query: "sum(rate(http_requests_total{job=\"my-app\"}[1m]))"

You can mix multiple triggers in the same ScaledObject; KEDA will use the highest calculated replica count across all triggers.

Scaling Jobs with ScaledJob

For workloads that run to completion (batch processing), KEDA offers ScaledJob. Instead of maintaining a pool of long-running pods, it creates a new Kubernetes Job for each event or group of events. This is perfect for processing files from blob storage, messages in a queue as individual tasks, or one-off data transformations.

Example ScaledJob that processes one job per message in a RabbitMQ queue:

apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
  name: orders-job-scaler
spec:
  jobTargetRef:
    template:
      spec:
        containers:
        - name: processor
          image: my-job-processor:latest
          env:
          - name: QUEUE_MESSAGE
            value: "{{.Message}}"  # injected event data
        restartPolicy: Never
    backoffLimit: 3
  triggers:
  - type: rabbitmq
    metadata:
      host: "amqp://guest:guest@rabbitmq-service.default.svc.cluster.local:5672/"
      queueName: "orders"
      mode: "QueueLength"
      value: "1"   # one job per message
  minReplicaCount: 0
  maxReplicaCount: 100
  scalingStrategy:
    strategy: "accurate"   # or "immediate"

Best Practices for Production

TriggerAuthentication and Secrets

To avoid exposing credentials in plain text, create a TriggerAuthentication resource that references Kubernetes secrets:

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: rabbitmq-auth
spec:
  secretTargetRef:
  - parameter: host
    name: rabbitmq-credentials
    key: connection-string

Then reference it in the ScaledObject trigger:

triggers:
- type: rabbitmq
  authenticationRef:
    name: rabbitmq-auth
  metadata:
    queueName: "orders"
    mode: "QueueLength"
    value: "10"

This keeps your ScaledObject portable and secure. The same pattern works for Kafka, Azure services, AWS, and any scaler requiring sensitive parameters.

Monitoring and Observability

KEDA's operator exposes metrics at port 8080, path /metrics. You can scrape them with Prometheus by adding a ServiceMonitor or a PodMonitor. Key metrics include:

Example Prometheus scrape configuration snippet:

- job_name: 'keda'
  static_configs:
  - targets: ['keda-operator.keda.svc.cluster.local:8080']

These metrics help you alert on scaler failures and visualize event-driven scaling patterns.

Cooldown and Scaling Windows

KEDA respects the cooldownPeriod (in seconds) after a scale-up before allowing scale-down. Additionally, the underlying HPA has its own stabilization windows (--horizontal-pod-autoscaler-downscale-stabilization). For precise control, you can set HPA behaviors in the ScaledObject's advanced section:

advanced:
  horizontalPodAutoscalerConfig:
    behavior:
      scaleDown:
        stabilizationWindowSeconds: 300
        policies:
        - type: Percent
          value: 50
          periodSeconds: 60

This allows granular tuning, but the defaults are usually sufficient for most workloads.

Conclusion

KEDA brings event-driven intelligence to Kubernetes autoscaling, bridging the gap between resource-based HPA and real-world event sources. By scaling based on queue lengths, message rates, or custom metrics, you can build truly elastic systems that right-size themselves to actual demand — even scaling down to zero when idle. The implementation pattern is consistent across scalers, making it easy to adopt for diverse event sources like Kafka, RabbitMQ, Prometheus, and cloud services.

Start by installing KEDA, wrapping your deployment with a ScaledObject, and watching it react to events. Follow best practices around secure authentication, resource limits, and cooldown tuning to ensure stable, cost-efficient production behavior. With KEDA, you unlock the full promise of event-driven architectures on Kubernetes — responsive, resource-efficient, and infinitely scalable.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles