KEDA: Event-Driven Autoscaling in Kubernetes
A zero-to-production guide to scaling workloads by what is actually happening in your system: queue depth, stream lag, schedules and custom metrics, including scaling all the way down to zero and back.
Picture a busy pizza shop. On a quiet Tuesday afternoon, one cook is plenty. On Friday night, orders pour in and you need eight cooks. A smart manager does not wait until the kitchen looks hot and sweaty before calling in help. The manager looks at the order screen. When fifty tickets pile up, more cooks are called. When the screen is empty for an hour, most cooks go home and the shop saves money on wages.
Most Kubernetes clusters do not have a manager like that. The built-in autoscaler looks at how hot the kitchen is, meaning how much CPU and memory the running containers are using, not at how many orders are waiting. For many applications that is the wrong signal. A worker that reads messages from a queue may use almost no CPU while a million messages pile up behind it. A nightly report generator may need to run only when a file arrives. An API that is used only during business hours may sit idle, and still cost money, for sixteen hours a day.
KEDA, which stands for Kubernetes Event-Driven Autoscaling, is the manager who reads the order screen. It watches real events and real backlogs from more than fifty kinds of systems, and tells Kubernetes how many copies of your application to run, including none at all. This guide teaches KEDA from the very beginning. Every new word is explained when it first appears, every big idea comes with an everyday comparison, and each chapter builds on the previous one. By the end you will know how to design, secure, tune and operate event-driven autoscaling in production.
1Why CPU-Based Scaling Is Not Enough
Before we meet KEDA, we need to see the problem it solves. This chapter shows where the standard approach to autoscaling works well, and where it quietly fails.
What autoscaling means
Autoscaling means that a system adds or removes capacity automatically as demand changes. In Kubernetes, the unit of capacity is usually a Pod. A Pod is the smallest runnable piece in Kubernetes, and it holds one or more containers. If demand goes up, we want more Pods. If demand goes down, we want fewer Pods, so we are not paying for machines that do nothing.
The classic signal: CPU and memory
The standard Kubernetes autoscaler looks at how busy each Pod is. If the average CPU use across Pods is high, it adds Pods. If it is low, it removes some. This works nicely for web servers that do real work for every request, because more requests mean more CPU use.
Where the classic signal breaks
- Queue workers. A worker takes a message, calls a slow outside service, and waits. While it waits it uses almost no CPU. Ten thousand messages can be waiting in the queue while CPU shows a calm 5 percent, so no new workers start.
- Stream processors. A program that reads from a stream such as Apache Kafka falls behind when data arrives faster than it can process. The measure that matters is lag, the number of unread messages, which CPU cannot show.
- Scheduled work. Some applications are needed only at known times, such as a trading dashboard from 9 a.m. to 5 p.m. CPU only reacts after the rush has already started.
- Rare events. A function that runs when a file is uploaded may be needed ten times a day. Keeping one Pod running all day just to wait is waste. Plain CPU-based scaling cannot go below one Pod.
- Delayed reaction. CPU rises only after a flood of work has arrived, so the system always starts scaling late.
Judging a restaurant’s need for staff by how sweaty the cooks look is like judging a queue by CPU. If the cooks are waiting for the oven, they look relaxed even though fifty customers are lined up outside. The right signal is the length of the line, not the mood of the kitchen.
Scale on the signal that best predicts the work waiting to be done. For web servers that may be CPU or request rate. For event-driven systems it is almost always a backlog, a lag, a rate or a schedule.
2Kubernetes Autoscaling Basics
KEDA does not replace Kubernetes autoscaling. It builds on it. To understand KEDA you must first understand the parts it connects to.
The three built-in autoscalers
Horizontal Pod Autoscaler
Changes the number of Pods. Adds copies when load rises and removes them when it falls. This is the one KEDA works with.
Vertical Pod Autoscaler
Changes the size of each Pod, meaning how much CPU and memory it requests, instead of how many Pods there are.
Cluster Autoscaler
Adds or removes the machines, called nodes, that Pods run on. It reacts when Pods cannot be placed for lack of room. Newer tools such as Karpenter do a similar job.
How the HPA decides
The Horizontal Pod Autoscaler, or HPA, is a control loop that runs inside Kubernetes. By default it wakes up about every 15 seconds. Each time, it asks a metrics source for a number, compares it with a target, and calculates how many Pods it wants. The idea behind the calculation is simple: if you have 4 Pods and each is at double the target load, you need about 8 Pods. If each is at half the target, you need about 2.
Where the HPA gets its numbers
The HPA can read three kinds of metrics. Resource metrics are CPU and memory, served by a component called the metrics server. Custom metrics describe objects inside the cluster, such as requests per second on a Pod. External metrics come from systems outside the cluster, such as the length of a cloud queue. Kubernetes defines an official programming interface for external metrics, so any program can register itself to answer questions about them.
What the HPA cannot do alone
- It needs a program that actually fetches metrics from queues, streams and databases, and translates them into the official interface. Writing such an adapter for every system is a lot of work.
- It cannot scale a workload to zero by default. Its minimum is one Pod.
- It cannot create short-lived jobs in response to individual events.
The HPA is a very good thermostat. It can turn heating up or down perfectly, but only if someone gives it a thermometer reading. KEDA is the set of thermometers for every room and every kind of weather that your thermostat did not know how to read.
KEDA does not do the HPA’s job a second time. It uses the real Kubernetes HPA for scaling between one Pod and many Pods, and adds its own logic for the step between zero and one. This split is explained in detail in Chapter 9.
3Events, Queues and Streams: A Quick Primer
KEDA is named after events, so it helps to know what an event-driven system looks like and which signals it produces.
What an event-driven system is
In an event-driven system, parts of the application talk to each other by sending small messages called events, instead of calling each other directly and waiting for answers. A shop website, for instance, does not charge the card, reserve stock, send email and update analytics in one long call. It drops an “order placed” event into a queue, and separate workers pick it up and do their own part. This design makes systems more flexible and more resilient, because a slow worker does not hold up the website.
Queues and streams
A queue is a line of messages. Producers add messages at the back, and consumers take them from the front. Each message is usually handled by one consumer and then removed. Examples include Amazon SQS, RabbitMQ and Azure Service Bus. A stream or log is a long record of events that consumers read at their own pace, each keeping track of its position. Apache Kafka is the best-known example. The gap between the newest event and the position a consumer has reached is called lag.
Signals that event-driven systems produce
| Signal | What it tells you | Typical source |
|---|---|---|
| Queue length | How many messages are waiting to be handled | SQS, RabbitMQ, Service Bus, Redis lists |
| Consumer lag | How far behind consumers are in a stream | Kafka, Pulsar, Kinesis |
| Message rate | How fast messages arrive or are handled | Prometheus metrics, broker metrics |
| Time | Whether the current time is inside a busy window | Cron-style schedules |
| Custom value | Any business number you can measure | Databases, HTTP endpoints, monitoring systems |
A queue is like the line at a bank. Each customer is served once and leaves. A stream is like a recorded TV series: many viewers can watch, each at their own place in the story, and the “lag” is how many episodes you are behind the latest one. Different shapes, but both tell you how much work is waiting for you.
4What KEDA Is and Where It Came From
Now we can describe KEDA properly: what it does, who made it, and how it fits with the tools you already use.
A short history
KEDA was started in 2019 as a joint project between Microsoft and Red Hat. Microsoft had been building Azure Functions, a service that runs small pieces of code in response to events, and wanted the same event-driven scaling to work on any Kubernetes cluster. The project was accepted into the Cloud Native Computing Foundation (CNCF) as a sandbox project in 2020, moved up to incubating in 2021, and reached the highest level, graduated, in 2023. Graduation means the project has proven maturity, broad adoption and healthy governance.
What KEDA does in one sentence
KEDA is a lightweight add-on for a Kubernetes cluster that watches external event sources and adjusts the number of Pods of your workloads, from zero up to as many as you allow, based on what it sees.
What KEDA is not
- It is not a message queue. It reads from queues you already have.
- It is not a replacement for the HPA. It feeds and controls the HPA.
- It is not a node autoscaler. It adds Pods, and other tools add the machines they run on.
- It does not require you to rewrite your application. The application keeps doing its normal job, and KEDA decides how many copies run.
Why teams choose it
Many event sources
Well over fifty built-in scalers cover queues, streams, databases, monitoring systems, cloud services and schedules.
Scale to zero
Idle workloads can run no Pods at all, so you pay nothing for compute while nothing happens.
One small object
A single configuration object describes the target, the limits and the triggers. No custom code is needed.
Built on the HPA
It reuses the proven Kubernetes scaling engine and does not invent a competing one.
Works anywhere
It runs on any conformant Kubernetes cluster, in any cloud or in your own data center.
Open source and extensible
Anyone can add a new scaler or write an external one in any language.
KEDA is pronounced like “KEH-duh”. The name is an acronym, and you will often see people write it in capitals. It is sometimes confused with Knative, another event-oriented Kubernetes project. We compare them in Chapter 24.
5Architecture and Components
KEDA is made of a few small programs. Each has one clear job, and together they bridge the world of events and the world of Pods.
The big picture
When you install KEDA, it creates a namespace, usually called keda, and starts three groups of Pods there. All your application workloads stay in their own namespaces. KEDA only reaches into them to read their scale and change their replica count.
flowchart TB
subgraph Sources["External Event Sources"]
Q["Message queue"]
K["Kafka stream"]
P["Prometheus metrics"]
C["Cron schedule"]
end
subgraph Cluster["Kubernetes Cluster"]
API["API Server"]
HPA["Horizontal Pod Autoscaler"]
subgraph KEDA["keda namespace"]
OP["KEDA Operator - controller and scalers"]
MS["Metrics API Server - external metrics adapter"]
WH["Admission Webhooks"]
end
subgraph App["Application namespace"]
SO["ScaledObject"]
TA["TriggerAuthentication"]
DEP["Deployment and Pods"]
end
end
SO --> OP
TA --> OP
OP -->|"poll for events"| Q
OP -->|"poll for events"| K
OP -->|"poll for events"| P
OP -->|"poll for events"| C
OP -->|"scale 0 to 1 and 1 to 0"| DEP
OP -->|"create and manage"| HPA
HPA -->|"ask for external metrics"| API
API -->|"forward request"| MS
MS -->|"fetch values"| OP
HPA -->|"scale 1 to N"| DEP
WH -->|"validate objects"| API
Component by component
| Component | What it does | Why it exists |
|---|---|---|
| KEDA operator | Watches KEDA objects, polls event sources through scalers, activates and deactivates workloads (zero to one and back), and creates the HPA objects. | It is the brain, and handles the part the HPA cannot: scaling to and from zero. |
| Metrics API server | Answers the HPA’s questions about external metrics by passing along the values the operator collects. | It plugs KEDA into the official Kubernetes external-metrics interface. |
| Admission webhooks | Check KEDA objects when they are created or changed and reject mistakes such as two scalers pointing at the same workload. | Catches configuration errors early, before they cause confusing behavior. |
| Custom resource definitions | Add new object kinds such as ScaledObject, ScaledJob and TriggerAuthentication to the cluster. | Lets you manage scaling like any other Kubernetes object. |
| Scalers | Small plug-in modules inside the operator, one per kind of event source. | Each knows how to ask one specific system “how much work is waiting?” |
Think of KEDA as a shop manager with an assistant and an inspector. The operator is the manager who watches the order screens and decides when to open or close the kitchen. The metrics server is the assistant who reports the order numbers to the shift supervisor, the HPA. The admission webhooks are the inspector who checks every new instruction for mistakes before it is accepted.
6The ScaledObject: Your Main Control Panel
Almost everything you do in KEDA starts with one object. Learn its parts, and you can read any KEDA configuration you will ever meet.
What a ScaledObject is
A ScaledObject is a Kubernetes object that tells KEDA “scale this workload, between these limits, based on these triggers.” It lives in the same namespace as the workload. You create one for each application you want KEDA to manage.
The important settings, in plain words
| Setting | Meaning | Default |
|---|---|---|
| Scale target | Which workload to scale. Usually a Deployment, but a StatefulSet or any custom resource that supports the scale interface also works. | Required |
| Triggers | One or more event sources and the thresholds that decide scaling. | Required |
| Minimum replicas | The fewest Pods to keep. Setting zero allows scale to zero. | 0 |
| Maximum replicas | The most Pods allowed. This is your safety ceiling. | 100 |
| Polling interval | How often, in seconds, KEDA checks the event source. | 30 seconds |
| Cooldown period | How long, in seconds, to wait after the last activity before scaling from one Pod down to zero. | 300 seconds |
| Fallback | What to do if KEDA cannot reach the event source. Covered in Chapter 15. | Not set |
| Advanced options | Fine tuning of the underlying HPA, such as how fast it may scale down. | HPA defaults |
A worked example in words
Imagine a service called “invoice-worker” that reads invoices from an SQS queue and turns them into PDF files. You write a ScaledObject that points at the invoice-worker Deployment. You set the minimum to zero and the maximum to 40. You add one trigger of type SQS queue, naming the queue and saying “target ten messages per Pod.” From then on, if 200 messages are waiting, KEDA wants 20 Pods. If the queue is empty for five minutes, it shrinks the Deployment to zero.
Name the target
Point the ScaledObject at the Deployment that holds your worker.
Set the limits
Choose a minimum and maximum. The maximum protects downstream systems from being overwhelmed.
Add triggers
Describe where the work comes from and how much work one Pod should handle.
Provide credentials
Reference a TriggerAuthentication if the event source needs a password or identity. See Chapter 8.
Never create your own HPA for a workload that a ScaledObject manages. KEDA creates and owns an HPA for it. Two autoscalers pulling the same Pods in different directions causes flapping, and KEDA’s admission webhook will usually reject the attempt.
7Scalers: The Library of Event Sources
A scaler is the part of KEDA that knows how to talk to one kind of system. This is where KEDA gets its enormous reach.
What a scaler does
A scaler connects to a particular system, asks a question such as “how many messages are waiting?”, and returns two things: a number, and a yes-or-no answer to “is there any work at all?” The yes-or-no answer is called activity. It decides whether the workload should be running at all. The number drives how many Pods to run.
The families of scalers
| Family | Examples | What it measures |
|---|---|---|
| Message queues | Amazon SQS, RabbitMQ, Azure Service Bus, Google Pub/Sub, IBM MQ | Number of waiting messages |
| Streams and logs | Apache Kafka, Apache Pulsar, AWS Kinesis, Azure Event Hubs | Consumer lag behind the newest event |
| Databases and caches | Redis lists and streams, PostgreSQL, MySQL, MongoDB, Elasticsearch | Row counts, list lengths, or the result of a query |
| Monitoring systems | Prometheus, Datadog, New Relic, Graphite | Any metric expressed as a query |
| Cloud services | Azure Blob, Azure Pipelines, AWS CloudWatch, Google Cloud Storage | Files, builds and cloud metrics |
| Time-based | Cron | Whether the clock is inside a scheduled window |
| Resource and generic | CPU, memory, metrics API, external gRPC | In-cluster usage, any HTTP endpoint, or your own code |
Two numbers every trigger has
Almost every trigger has two main thresholds. The target value says how much work one Pod should handle, for example “ten messages per Pod.” This drives the Pod count once the workload is running. The activation value says how much work must exist before the first Pod is started at all, for example “at least one message.” Keeping these two ideas apart makes scale-to-zero behavior predictable. Chapter 10 explains why.
Scalers are like different kinds of gauges in a control room. One reads the pressure in a pipe, another the temperature of a tank, another the water level behind a dam. They all report in different units, but the control room operator, the HPA, only needs one thing from each gauge: a number saying how much action is needed.
If no built-in scaler fits your system, you have two options. You can use the Prometheus or generic metrics-API scalers to read a custom number, or you can write an external scaler, a small gRPC service that KEDA calls. Both are covered in Chapter 17.
8Authentication: Letting KEDA Read Your Event Sources
Most queues and databases need a password or identity. KEDA offers safe ways to provide them without putting secrets into scaling configuration.
The problem
To count messages in a private queue, KEDA must prove who it is. If you write the connection string straight into the ScaledObject, anyone who can read that object can read your secret. We want the credentials stored in one safe place and referenced by name.
TriggerAuthentication
A TriggerAuthentication is a separate KEDA object that describes how to log in. A trigger refers to it by name. The authentication object can pull values from several places:
- Kubernetes Secrets. The simplest option. The object points at a key inside a Secret.
- Environment variables of the target workload. KEDA reads values the workload already uses.
- Secret stores such as HashiCorp Vault. Values are fetched from a central vault at runtime.
- Cloud workload identity. KEDA uses an identity from the cloud provider, such as Microsoft Entra workload identity on Azure, IAM roles for service accounts on AWS, or workload identity on Google Cloud. No stored password exists at all.
Namespaced and cluster-wide versions
A TriggerAuthentication lives in one namespace and can be used only by ScaledObjects in that namespace. A ClusterTriggerAuthentication is cluster-wide and can be shared. Use the namespaced version by default. It limits the damage if one team misconfigures something.
Instead of writing your door code on the delivery instructions, you give the courier a pass card from the building manager. The courier shows the card and gets in. Workload identity is the pass card. A stored password is the code written on the note, which anyone who sees it can copy.
Prefer identity-based authentication wherever your cloud supports it. Passwords and keys can leak, be copied, or expire at awkward times. An identity that is granted only the single permission to read queue length is far safer than a key that can also read and delete messages.
9Internal Working: Who Does What, and When
The heart of KEDA is a clever division of labor between two different scaling mechanisms. Understanding this split explains almost everything about how KEDA behaves.
Two halves of the scaling range
Scaling a workload really has two separate problems. The first is deciding whether the workload should run at all: is it at zero, or is it at one or more? The second is deciding how many copies to run once it is awake. KEDA solves them in different places.
- Between zero and one: the KEDA operator decides. The HPA cannot handle zero, so KEDA does it directly by changing the replica count of your workload.
- Between one and many: the Kubernetes HPA decides. KEDA creates an HPA for you, and the HPA asks KEDA’s metrics server for the numbers it needs.
sequenceDiagram
autonumber
participant S as Event Source
participant O as KEDA Operator
participant D as Deployment
participant H as HPA
participant M as KEDA Metrics Server
loop every polling interval
O->>S: How much work is waiting?
S-->>O: Value and activity
end
alt Work found and replicas are zero
O->>D: Scale from 0 to 1
end
loop every HPA sync period
H->>M: Request external metric value
M->>O: Get latest value
O-->>M: Current value
M-->>H: Metric value
H->>D: Set replicas between 1 and max
end
alt No activity for the cooldown period
O->>D: Scale from 1 to 0
end
Step by step
You create a ScaledObject
The webhook validates it. The operator notices it, builds a scaler for each trigger, and creates an HPA named after the ScaledObject.
The operator polls
Every polling interval it asks each scaler for a value and an activity flag, and keeps the latest answers in memory.
Activation
If at least one trigger is active and the workload is at zero, the operator sets the replica count to one. This is the wake-up call.
The HPA takes over
With one or more Pods running, the HPA asks the metrics server for the current value and computes how many Pods are needed.
Deactivation
When every trigger reports no activity, a timer starts. If the cooldown period ends without new activity, the operator scales the workload to zero.
Think of a night-shift pizza shop. The owner, KEDA’s operator, decides whether to open the doors at all by watching the phone for the first order. Once the shop is open, the shift supervisor, the HPA, decides how many cooks are needed based on the number of tickets. When no tickets come for a long time, the owner locks up again.
Two clocks are always running: the operator’s polling interval, which defaults to 30 seconds, and the HPA’s sync period, which defaults to about 15 seconds. Total reaction time is the sum of these delays plus Pod startup. Chapter 21 shows how to shorten it.
10Scaling to Zero and the Idea of Activation
Going to zero is the most talked-about KEDA feature. It saves money, but it also changes how your system behaves, so it deserves a careful look.
Why zero matters
A workload with a minimum of one Pod costs money all day, even when nothing happens. If you have 200 small worker services and each sits idle 90 percent of the time, you pay for 200 always-on Pods to do almost nothing. With scale to zero, idle services use no CPU and no memory, and the cluster can shrink to fewer machines, which is where real savings appear.
Activation versus target
Each trigger in KEDA answers two different questions, and each question has its own threshold:
Should the workload be awake?
This uses the activation value. If the measured number is above it, the trigger is active and the operator wakes the workload. If it is at or below, the trigger is inactive.
How many copies are needed?
This uses the target value. Once the HPA is in charge, it divides the measured number by the target to decide the Pod count.
By default the activation value is zero, which means “wake up when there is any work at all.” You can raise it to avoid waking the workload for a trivial amount of work. For example, if starting the workload is expensive, you might say “do not wake unless at least 50 messages are waiting.”
The cooldown period
When the work disappears, KEDA does not shut down at once. It waits for the cooldown period, 300 seconds by default, with all triggers inactive before going from one to zero. This prevents a cycle of waking and sleeping if messages arrive in short bursts. Note that the cooldown applies only to the last step down to zero. Scaling from ten Pods to three is controlled by the HPA’s own rules.
The cost of sleeping: cold starts
When a workload is at zero and the first message arrives, someone must start a Pod, pull its image, start the program and make it ready. That can take from a few seconds to a few minutes. During that time the message simply waits in the queue. For background workers this is perfectly fine. For anything a person is waiting for, such as a web page, a cold start may be too slow. This is why scale to zero shines for queue and batch work, and why it needs extra care for interactive traffic (see the HTTP add-on in Chapter 17).
Benefits of zero
- No compute cost while idle
- Fewer nodes needed across many small services
- Less noise and less clutter in the cluster
- Good fit for queue-driven and scheduled work
Costs of zero
- First request or message waits for a cold start
- Caches and connections are lost on each sleep
- Monitoring has nothing to scrape while it sleeps
- Needs care with startup time and image size
If your minimum replica count is set above zero, KEDA never scales to zero, and the activation value only matters for edge cases. Teams sometimes set a minimum of one to be “safe” and then wonder why their bill did not drop. Decide on purpose.
11The Scaling Math Made Simple
You do not need advanced mathematics to predict what KEDA will do. One short idea explains nearly every scaling decision.
The basic rule
For queue-like triggers, the HPA uses an “average value per Pod” idea. It divides the total metric value by the target value per Pod, and rounds up. If 230 messages are waiting and your target is 10 messages per Pod, then 230 divided by 10 is 23 Pods. If 231 messages are waiting, the result is 23.1, which rounds up to 24 Pods. Rounding up means the system always leans toward having enough capacity.
Then the limits apply
After the calculation, the result is held between your minimum and maximum. If the math says 300 Pods and your maximum is 40, you get 40. If the math says 0 and your minimum is 2, you get 2. Remember that if the minimum is zero, the HPA itself works from one upward, and the operator handles the last step to zero.
The tolerance band
The HPA does not react to tiny changes. By default, if the ratio between the measured value and the target is within about 10 percent of one, it does nothing. This prevents constant small adjustments. With 23 Pods running and a queue that grows slightly, you may not see a change until the number moves enough to cross this band.
Choosing a good target value
The target is the most important number you pick. Ask yourself: how many messages can one Pod finish in a reasonable time? If one Pod handles 5 messages per second and you want the queue cleared within 20 seconds, one Pod can clear about 100 messages in that time, so a target near 100 messages per Pod would keep the queue short. If your messages are slow and heavy, the target might be 5. Measure first, then set the target, and check it under real load.
If each cashier can handle ten customers during your target waiting time and 230 customers are in line, you need 23 cashiers. If there are 231, you need a 24th, because you cannot hire two-tenths of a cashier. The tolerance band is the manager deciding not to call in a new cashier for just one or two extra customers.
For metrics that are rates or percentages rather than counts, such as requests per second from Prometheus, the same idea holds. The total is compared with the target per Pod. The difference between “average value” and “plain value” metric types can change the math, so check which type your trigger uses.
12ScaledJob: One Job per Unit of Work
Sometimes the right answer is not “more copies of a long-running program” but “one short-lived task for each piece of work.” KEDA has a second main object for exactly that.
Deployments versus Jobs
A Deployment runs Pods that live until they are told to stop. A Kubernetes Job runs a Pod to completion and then lets it exit. Which is better for event-driven work? It depends on how long each task runs and how safe it is to interrupt.
| Question | ScaledObject with a Deployment | ScaledJob |
|---|---|---|
| What runs? | Long-lived worker Pods that pull many messages | A new Job per batch of events, which ends when finished |
| Typical task length | Milliseconds to a few minutes | Minutes to hours |
| What happens on scale-down? | Pods are terminated, perhaps in the middle of work | Running Jobs are never killed by scaling; they finish |
| Best for | Steady stream of small messages | Heavy tasks such as video conversion, machine learning, reports |
How a ScaledJob works
A ScaledJob looks similar to a ScaledObject but, instead of a scale target, it holds a template for the Job to create. KEDA polls the triggers on each polling interval. If it finds work, it creates Jobs, never more than the maximum replica count you set. Each Job runs its Pod to completion, and finished Jobs are kept for a while according to history limits so you can read their logs, and then cleaned up.
Scaling strategies
One subtle question is how many new Jobs to start when some are already running. KEDA offers several strategies:
Subtract running Jobs
Starts enough new Jobs to cover the queue length minus the Jobs already running. Simple, and fine for most cases.
Count in-flight messages
Takes into account that running Jobs have already claimed messages, so the queue length alone is misleading. Good with queues where messages become invisible while being processed.
Your own formula
Lets you tune the calculation with a multiplier and an offset when the defaults do not match your workload.
Use all free slots
Starts as many Jobs as there is room for, so that a large backlog is attacked as fast as the limits allow.
Use Case: Video transcoding at a media company
Each uploaded video must be converted into several formats. A conversion takes between 5 and 40 minutes and must never be interrupted halfway. The team uses a ScaledJob triggered by the upload queue. Each Job takes one video, converts it and exits. Peak hours create dozens of Jobs at once, and quiet hours create none, so there are no idle converters.
If your work items are long and not safe to interrupt, a ScaledJob is the right tool. If they are short and your program can finish its current message when told to shut down, a ScaledObject with a Deployment is simpler and starts work faster, since the Pods are already warm.
13Data Flow and Lifecycle of a Scaling Event
Let us follow one burst of work from the moment it arrives until the system returns to rest. This ties together everything you have learned so far.
flowchart LR
A["Burst of messages arrives"] --> B["Queue length rises"]
B --> C["Operator polls and sees activity"]
C --> D{"Replicas at zero?"}
D -->|"yes"| E["Operator sets replicas to 1"]
D -->|"no"| F["HPA already running"]
E --> F
F --> G["HPA requests metric from metrics server"]
G --> H["Desired replicas computed"]
H --> I["Deployment scaled up"]
I --> J["New Pods start and pull messages"]
J --> K["Queue drains"]
K --> L["HPA scales down after stabilization window"]
L --> M{"No activity for cooldown period?"}
M -->|"yes"| N["Operator sets replicas to 0"]
M -->|"no"| K
Where time is spent
| Stage | Typical delay | Can you shorten it? |
|---|---|---|
| Operator notices new work | Up to one polling interval (30 seconds by default) | Yes, by lowering the polling interval |
| HPA reads the new value and decides | Up to about 15 seconds | Rarely; it is a cluster-wide setting |
| Pod scheduled onto a node | Seconds if room exists; minutes if a new node must start | Yes, with spare capacity or fast node provisioning |
| Image pulled and container started | Seconds to minutes | Yes, with small images and cached layers |
| Application warm-up | Depends on the program | Yes, with faster startup and readiness checks |
Where state lives
KEDA keeps little state of its own. Your configuration lives in Kubernetes objects. The latest metric values live in the operator’s memory and are rebuilt after a restart. The HPA that KEDA creates is a normal Kubernetes object, so you can inspect it with ordinary tools. The status of each ScaledObject, including whether it is ready, active or in fallback, is written onto the object and visible through kubectl.
Think of a fire brigade. The alarm rings (event), the dispatcher notices (polling), the trucks leave the station (Pod scheduling), they arrive and get to work (application warm-up), and when the fire is out they return and the station goes quiet (scale down). The time from alarm to first water is the sum of every stage, which is why fire stations invest in speeding up each one.
14Multiple Triggers, Schedules and Combining Signals
Real systems rarely depend on a single signal. KEDA lets you combine several, including time of day, in one object.
How several triggers combine
A ScaledObject can have many triggers. Each one independently proposes a Pod count. The HPA takes the highest of them. So if the queue trigger wants 8 Pods and the Prometheus trigger wants 12, you get 12. For activation, a workload wakes from zero if any single trigger is active, and goes back to sleep only when all are inactive. This “highest wins, any wakes it” behavior is safe by design: the system leans toward having enough capacity.
The cron scaler: scheduled capacity
The cron scaler turns the clock into a trigger. You give it a time zone, a start time, an end time and a desired number of Pods. Between the start and end, KEDA holds at least that many Pods. Outside the window, the cron trigger is inactive. This is perfect for predictable patterns: more capacity before the 9 a.m. rush, less overnight, and a bump before a weekly batch.
Use Case: Pre-warming before a sale
An online retailer knows its flash sale starts at noon every Friday. A cron trigger raises the minimum to 20 Pods at 11:50, so the app is warm when the first customers arrive. A queue trigger on the same ScaledObject handles anything beyond that. At 14:00 the cron window ends and the queue trigger alone controls the scale. The team gets both predictable readiness and flexibility for surprises.
Combining signals wisely
- Queue plus cron. Scheduled floor, event-driven growth above it.
- Queue plus CPU. Scale on backlog, with CPU as a safety valve if the work turns out heavier than expected.
- Kafka lag plus Prometheus latency. Catch both a growing backlog and slow processing.
Using a CPU or memory trigger together with a scale-to-zero setup can confuse things. These triggers cannot work when no Pods exist, since there is nothing to measure. Pair them with an event-based trigger that can wake the workload, and treat CPU as a secondary signal.
15Fallback, Pausing and Custom Formulas
What happens when the thing KEDA depends on breaks? And how do you take manual control when you need it? These features answer both questions.
Fallback: a safe default when the source is down
Suppose KEDA cannot reach your message broker because of a network problem. Without a plan, it has no number, and the workload might stay stuck at whatever size it happened to have. The fallback feature lets you say: “if the scaler fails this many times in a row, hold the workload at this fixed number of Pods.” You pick the failure threshold and the replica count. When the source recovers, normal scaling resumes. The ScaledObject status shows a fallback condition while this is active, so monitoring can alert on it. Fallback works with metrics measured as an average per Pod, so check your trigger type.
Pausing autoscaling
Sometimes you want to freeze scaling, perhaps during a maintenance window, a debugging session or a data migration. KEDA supports annotations on the ScaledObject. One annotation pauses scaling and leaves the replica count where it is. Another pauses scaling and forces a specific number of replicas, which can even be zero. Removing the annotation resumes normal behavior. This is much safer than deleting the ScaledObject, since deletion hands control of the replica count back to whatever was there before.
Scaling modifiers: building your own formula
Newer versions of KEDA allow you to combine several triggers into one calculated value. Instead of “highest wins,” you write a small formula, for example a weighted sum of queue length and processing time. This is a power tool. It lets you express rules such as “scale on queue length, but only when error rate is low.” Because it is a newer feature, check the current KEDA documentation for its status and syntax before relying on it in production.
Fallback
Keeps a known-safe number of Pods when metrics cannot be read.
Pause annotations
Freeze or pin the replica count without deleting anything.
Scaling modifiers
Combine triggers with a formula when simple highest-wins is not enough.
Status conditions
Ready, Active and Fallback flags tell you what KEDA thinks is happening.
Fallback is the “if the radio goes silent, keep the engine at cruising speed” rule on a ship. Pausing is the captain taking the wheel from the autopilot. Scaling modifiers are a custom rule for the autopilot itself, such as “slow down when it is both foggy and busy.”
16Tuning Scale-Up and Scale-Down Behavior
Autoscaling that reacts too fast flaps up and down. Autoscaling that reacts too slowly causes backlogs. This chapter shows the dials that control the speed.
The HPA behavior settings
Because KEDA uses a real HPA, you can tune the HPA’s behavior through the ScaledObject’s advanced options. Two directions can be tuned separately:
- Scale up. By default, the HPA may double the number of Pods, or add four, whichever is larger, every 15 seconds. That is fast. You can slow it down to protect a database from a sudden surge in connections.
- Scale down. By default, the HPA looks back over the past five minutes and picks the highest recommendation before shrinking. This stabilization window prevents sudden drops after a short lull. You can lengthen it for spiky workloads, or shorten it to save money faster.
| Dial | Effect of raising it | Effect of lowering it |
|---|---|---|
| Polling interval | Fewer calls to the event source, slower reaction | Faster reaction, more load on the source, higher API cost |
| Cooldown period | Longer before scale to zero, fewer cold starts | Quicker savings, more cold starts |
| Scale-down stabilization | Steadier, avoids flapping | Quicker savings, risk of thrash |
| Scale-up rate limit | Gentler on downstream systems | Faster recovery from backlog |
| Target value per Pod | Fewer Pods, longer queues | More Pods, shorter queues, higher cost |
Graceful shutdown: do not lose work when scaling down
When the HPA removes a Pod, Kubernetes sends it a polite stop signal and waits for a grace period before forcing it to stop. A well-written worker catches the signal, finishes its current message, and exits. A poorly written one is cut off mid-task, and the message is either lost or processed twice. For queue workers, set the grace period long enough to finish one typical message, and make sure your program handles the signal. Also design handlers to be idempotent, meaning that running the same message twice does no harm.
Scaling down is like closing extra checkout lanes in a supermarket. A good store lets the cashier finish serving the customer at the till before closing the lane. A bad one switches off the till while the customer is paying. Graceful shutdown is the polite way to close a lane.
Tune with data, not guesses. Replay a realistic burst in a test environment, plot queue length and Pod count together, and adjust one dial at a time. The perfect settings for a bursty image-processing worker are rarely the right settings for a steady billing worker.
17Going Further: External Scalers, the HTTP Add-on and Custom Resources
The built-in scalers cover a great deal, but not everything. These extension points let KEDA reach almost any signal, including web traffic.
External scalers: write your own
If your system is unusual, for example an in-house job scheduler, you can write an external scaler. This is a small server that speaks a simple protocol over gRPC, a fast way for programs to call each other. KEDA calls it with questions such as “is there any work?” and “what is the current metric value?” The scaler can be written in any language, run anywhere inside or outside the cluster, and answers from any data source you like. There are two styles. In the pull style, KEDA asks on each polling interval. In the push style, the scaler keeps a connection open and tells KEDA the moment activity changes, which reacts faster for waking from zero.
The metrics-API and Prometheus scalers
Often you do not need new code at all. The Prometheus scaler lets you write a query against your monitoring system and scale on its result. This is how you scale on things like “orders per minute” or “99th percentile latency” with no extra programming. The metrics-API scaler does the same for any HTTP endpoint that returns a number in JSON. Together they cover a huge range of business signals.
The HTTP add-on: scaling web traffic, even to zero
Plain KEDA reacts to queues and metrics, not to individual web requests. For web applications there is a companion project, the KEDA HTTP add-on. It places a small proxy called an interceptor in front of your service. When a request arrives for an application that is asleep, the interceptor holds the request, tells KEDA to wake the application, and forwards the request when the first Pod is ready. The user sees a slow first response instead of an error. While running, the interceptor also counts requests, so scaling can follow real traffic. Check the project’s current maturity level before using it for critical user-facing paths, since the add-on has been developing more slowly than the core.
Scaling custom resources
KEDA can scale anything that supports the standard scale interface, not only Deployments. This includes StatefulSets and many custom resources created by operators, such as certain machine learning serving frameworks. As long as the object exposes how many replicas it wants, KEDA can drive it.
External scaler
Your own gRPC server for unusual event sources, in any language.
Prometheus scaler
Turn any monitoring query into a scaling signal.
Metrics API scaler
Read a number from any HTTP endpoint returning JSON.
HTTP add-on
Hold and forward requests so web apps can scale from zero.
The built-in scalers are the standard sockets on the wall. An external scaler is a travel adapter you build for a strange plug. The HTTP add-on is a receptionist who greets a visitor at a closed office, quickly calls the staff in, and then shows the visitor through when the lights are on.
18Installing KEDA and Running It on Cloud Platforms
KEDA is easy to install, and several platforms ship it ready to use.
Ways to install
- Helm chart. The most common method. One command installs the operator, the metrics server and the webhooks into their own namespace, and lets you set options through a values file.
- Plain manifests. The project publishes ready-made files you can apply directly, which suits teams that prefer fully explicit configuration.
- OperatorHub. On platforms that use operator catalogs, KEDA can be installed and upgraded through the catalog.
- Managed add-ons. Some cloud services offer KEDA as a built-in feature you switch on.
KEDA on major platforms
| Platform | How KEDA appears |
|---|---|
| Azure Kubernetes Service | A managed KEDA add-on that the cloud keeps up to date, and which works naturally with Azure workload identity. |
| Azure Container Apps | Scale rules in this serverless container service are built on KEDA, so the same concepts and scaler names apply. |
| Red Hat OpenShift | Supported as the Custom Metrics Autoscaler operator, a productized distribution of KEDA. |
| Amazon EKS and Google GKE | Installed through Helm or manifests. Identity features of each cloud let KEDA read queues and metrics without stored passwords. |
| Self-managed or on-premises clusters | Works anywhere. Use Secrets or a vault for credentials. |
What to decide before installing
- One KEDA per cluster. Only one metrics server can register the external-metrics interface in a cluster. If another adapter already owns it, you must resolve the conflict first.
- Watch scope. By default KEDA watches all namespaces. You can limit it to chosen namespaces in shared clusters.
- Resource requests. Give the operator enough memory for the number of ScaledObjects you plan to run.
- Node placement. Place KEDA on reliable nodes, not on spot machines that may disappear.
Other tools can also claim the external-metrics interface, including some monitoring adapters. If two programs try to register it, one of them fails silently and the HPA reports missing metrics. When installing KEDA on a cluster that already uses an adapter, check how both can coexist.
19Security: Least Privilege for an Autoscaler
KEDA can change how many Pods run and it holds the keys to your queues. That makes it a sensitive component, and it deserves careful protection.
What KEDA is allowed to do
To work, the operator needs permission to read ScaledObjects, to read and change the scale of target workloads, to create and update HPAs, and to read the Secrets that TriggerAuthentications reference. These permissions are broad by default. Anyone who controls KEDA can start or stop your workloads, and may be able to read credentials that scalers use.
Protecting the system
- Control who can create KEDA objects. Anyone who can create a ScaledObject can set the maximum replica count and cause a large bill or overload a downstream database. Use RBAC to limit these rights to trusted teams.
- Prefer identity over passwords. Use cloud workload identity so no secrets are stored, and grant only the narrowest permission, such as reading queue attributes.
- Use namespaced authentication. TriggerAuthentication objects stay inside their namespace. Use the cluster-wide kind only for shared credentials that you truly want every team to use.
- Limit the watch scope. In multi-tenant clusters, restrict the operator to namespaces that need it, or run separate controlled setups.
- Secure the metrics endpoint. The metrics server speaks through the Kubernetes aggregation layer. Keep its certificates valid and its network paths restricted.
- Apply network policies. Allow the operator to talk only to the API server and to the event sources it needs.
- Guard the supply chain. Install official images, pin versions and verify signatures where the project provides them.
- Cap the blast radius. Use admission rules, for example with a policy engine, to enforce a maximum replica count and require a reasonable polling interval across all ScaledObjects.
What people do
They put a full-access cloud key into a single shared cluster-wide authentication object and let every team use it.
Why it hurts
One leaked key now opens every queue in the account, and any team can accidentally read or drain another team’s queue.
Better approach
Give each team its own identity with read-only access to just its queue attributes, referenced from a namespaced authentication object.
Giving KEDA broad credentials is like giving a building’s night manager a master key to every office. He needs to switch the lights on and off, not to read your filing cabinet. Narrow keys limit what a mistake or a thief can do.
20Monitoring, Logging and Metrics
An autoscaler you cannot see is an autoscaler you cannot trust. KEDA gives you status, events, logs and metrics.
Status on the objects
Start with the ScaledObject itself. Its status shows conditions: whether it is ready, whether it is active, and whether fallback is in effect. It also shows the name of the HPA it created, the external metrics it provides and the time of the last activity. A quick description of the object, plus a look at the HPA, answers most “why did it scale?” questions.
Events and logs
KEDA records Kubernetes events when it activates or deactivates a workload and when something fails, for example when a scaler cannot connect. The operator and metrics server also write structured logs. When scaling seems stuck, the operator log usually names the scaler and the exact error, such as a rejected credential or an unreachable broker.
Prometheus metrics
KEDA exposes its own metrics in the Prometheus format. Exact names change between versions, so check the documentation for yours, but the useful families are:
Scaler metric value
The number each scaler last reported, such as queue length. Chart this next to Pod count.
Scaler active flag
Whether each trigger currently says there is work. Shows why a workload woke or slept.
Scaler and object errors
Counters of failed polls. A rise points to credential, network or source problems.
Scaler latency
How long the event source takes to answer. Slow answers delay scaling.
Dashboards and alerts
A single dashboard per workload that shows queue length, desired replicas, actual replicas, and processing time tells the whole story. Useful alerts include:
- A scaler reporting errors for several polls in a row.
- A workload in fallback mode.
- Queue length rising while replica count is at its maximum, meaning you have hit your ceiling.
- Queue length high but replicas at zero, meaning activation failed.
- Operator or metrics server Pods restarting.
- Oldest message age passing a service goal, which is often a better signal than length.
A hospital shows each patient’s chart at the bedside, with trend lines, not only the latest number. Your dashboard should show the queue and the Pod count as two lines on the same chart. When the queue climbs and the Pod line does not follow, you see the problem immediately.
21Performance and Scalability
KEDA must itself scale gracefully, and it must scale your workloads fast enough. Both questions come down to a few measurable things.
Reaction time: how quickly can you scale?
Total time from burst to capacity is the sum of polling delay, HPA delay, Pod scheduling, image pull and application warm-up. Shortening the polling interval to a few seconds helps, but each poll is a call to your event source. Calls to cloud queue services may cost money and are subject to rate limits, so a very short interval across hundreds of ScaledObjects can add up. Balance speed against cost and limits.
Pod startup is usually the bottleneck
Teams often tune the polling interval and forget that a Pod takes 90 seconds to start. Make start-up faster by keeping images small, using cached base layers, avoiding heavy work at start, and defining readiness checks that turn true as soon as the program can really accept work. If new Pods cannot be placed because the cluster is full, the node autoscaler must add machines, which can take minutes. Keep a small buffer of spare capacity for latency-sensitive workloads.
Scaling KEDA with the number of objects
- Many ScaledObjects. Each one has its own polling loop. Thousands are possible, but give the operator enough CPU and memory, and watch its logs for slow scalers.
- Slow event sources. One scaler that takes ten seconds to answer can delay others sharing its loop. Watch scaler latency.
- Shared queues. If twenty ScaledObjects poll the same broker, consider the total request rate that broker sees.
- Metrics server load. The HPA asks for metrics every few seconds for each ScaledObject, and the metrics server answers from cached values, so this is cheap, but it must stay healthy.
Protecting what sits behind the workers
The fastest autoscaling is not always the best. If 200 Pods wake up and all open connections to the same database, the database may fall over. Set the maximum replica count to what downstream systems can bear, limit how fast scale-up can happen, and use connection pooling.
Opening fifty extra checkout lanes does not help if there is only one door out of the store. The bottleneck simply moves. Before you speed up scaling, find what the extra workers will push against next.
Measure the time from “message enqueued” to “message processed” at the 95th and 99th percentile. That single number captures every delay from queue to worker and tells you if your scaling settings are good enough for your business goal.
22High Availability and Reliability
KEDA is on the path of your scaling, so you must know what happens when it stumbles and how to make it sturdy.
What breaks when KEDA is down
If the operator stops, workloads that are already running keep running at their current size. The HPA that KEDA created still exists, but it cannot get fresh metrics, so it holds steady or reports missing metrics. Scaling to and from zero stops, which is the more serious effect: a sleeping workload will not wake up while the operator is down, and messages wait in the queue. Because queues hold messages safely, the usual result is delay, not data loss. That makes event-driven systems naturally forgiving, provided the queue itself is durable.
Making KEDA itself highly available
- Run standby operator replicas. The operator supports leader election, so extra replicas wait in standby and take over quickly if the active one fails. Check the current documentation for how the metrics server should be replicated.
- Spread across zones. Use topology spread or anti-affinity so failure of one zone does not remove all KEDA Pods.
- Set disruption budgets. Prevent node upgrades from evicting every KEDA Pod at once.
- Use priority classes. Make sure KEDA is scheduled even when the cluster is under pressure.
- Use fallback. Give important ScaledObjects a safe replica number for when their event source cannot be read.
Designing workloads that recover well
Make your consumers idempotent, so that a message delivered twice is not harmful. Use dead-letter queues so a poisonous message does not block a worker forever. Use visibility timeouts or acknowledgments properly, so messages from a worker that is shut down mid-task return to the queue. These habits turn scaling events from risky moments into routine ones.
Why KEDA failures are mild
- Queues keep messages while workers are away
- Running Pods are not stopped by a KEDA outage
- Standby operators take over automatically
- Fallback sets a safe size
Where it can still hurt
- Sleeping workloads cannot wake while the operator is down
- Latency-sensitive paths feel delays quickly
- Bad credentials stop scaling silently unless alerted
- A wrong maximum can overload other systems
23Cost Optimization and FinOps
For many teams, the main reason to adopt KEDA is money. Here is how the savings actually appear, and how to avoid false economies.
Where the savings come from
- No idle Pods. Workloads at zero use no CPU or memory requests, so the scheduler can pack other work onto fewer machines.
- Right-sized peaks. Instead of provisioning for the worst day all month, capacity follows demand hour by hour.
- Fewer nodes. When the node autoscaler sees empty machines, it removes them. This is where the actual bill falls.
- Cheaper machine types. Interruptible workers can run on discounted spot or preemptible machines, because a queue safely absorbs interruptions.
The savings only appear if nodes shrink
Scaling Pods to zero saves nothing by itself if the machines underneath stay on. The money is saved when the node autoscaler removes unused nodes. Make sure the node layer is set up to scale down, and watch for Pods that block it, such as ones with local storage or restrictive disruption budgets.
Counting the costs on the other side
| Cost | Where it shows up | How to manage it |
|---|---|---|
| Cold starts | Slower first response after idle periods | Longer cooldown, smaller images, or a minimum of one for sensitive paths |
| Polling API calls | Cloud queue request charges and rate limits | Reasonable polling interval, shared metrics, push-style scalers |
| Image pulls | Network and registry costs for frequent restarts | Use a nearby registry and cached layers |
| Runaway scaling | A bug or poison messages create a huge Pod count | A sensible maximum, alerts, dead-letter queues |
Use Case: Cutting the cost of overnight batch workers
A data company ran 60 batch worker services around the clock, each with two Pods, though each did real work only for a couple of hours a day. After moving them to KEDA with scale to zero and a queue trigger, their daytime need fell to a fraction of the old total, and the node autoscaler removed over a third of the machines in the worker pool. The team kept one always-on Pod only for the two services whose users waited for responses.
Tag your ScaledObjects with team and cost-center labels, and export Pod-hours by workload. A report that shows “we saved this much last month” is the best way to keep support for the project.
24KEDA Compared with Other Approaches
KEDA is one of several ways to scale on demand. Knowing the alternatives helps you use each where it is strongest.
| Approach | Scales on | Scale to zero | Best for |
|---|---|---|---|
| Plain HPA | CPU, memory, and metrics from an adapter you install | No (by default) | Request-driven services with steady CPU patterns |
| HPA with a custom metrics adapter | Any metric the adapter exposes | Limited | Teams who already run a single adapter for one source |
| KEDA | Dozens of event sources, schedules and custom queries | Yes | Queue, stream, batch and scheduled workloads |
| Knative Serving | Incoming HTTP request concurrency and rate | Yes | Serverless web services with revision-based routing |
| Custom controller | Anything you code | If you build it | Highly specialized needs, at the price of maintenance |
| Provider serverless functions | Provider events | Yes | Small stateless tasks when you accept lock-in |
KEDA and Knative
The two projects solve different problems and can live side by side. Knative is a full serverless layer for web services: it routes traffic, manages revisions and scales mainly on request load. KEDA is a focused autoscaler for any workload and any event source, with no routing layer. Pick Knative when you want a serverless platform for HTTP services. Pick KEDA when you want to keep ordinary Deployments and Jobs and add event-driven scaling to them.
KEDA and node autoscalers
KEDA changes the number of Pods. A node autoscaler such as the Cluster Autoscaler or Karpenter changes the number of machines. They are partners: when KEDA asks for many new Pods and the cluster is full, Pods wait in a pending state, and the node autoscaler adds machines to fit them. A good design plans both layers together.
KEDA strengths
- Graduated CNCF project with wide support
- Huge catalog of scalers
- Scale to zero for any workload
- Uses the HPA and keeps familiar behavior
- No application rewrite needed
KEDA trade-offs
- One more component to run and secure
- Cold starts when scaling from zero
- Reaction time bounded by polling and Pod startup
- Web traffic needs an additional add-on
25Design Patterns and Anti-Patterns
Teams that run KEDA well tend to follow the same habits. Teams that struggle tend to fall into the same traps.
Patterns that work
Competing consumers
Many identical workers read from one queue. KEDA changes the number of workers, and the queue spreads the work.
Queue-based load leveling
Let a queue absorb bursts so the slow downstream system sees a steady flow, with KEDA keeping the backlog short.
Job per unit of work
Use ScaledJob for heavy, uninterruptible tasks so scale-down never kills work in progress.
Scheduled floor, event ceiling
A cron trigger guarantees capacity at known busy times, and event triggers handle the unexpected.
Dead-letter queue
Failed messages move aside after several attempts, so one bad message cannot keep the queue growing forever.
Backpressure with a ceiling
The maximum replica count matches what the database or partner API can handle, so scaling never causes a second outage.
Patterns that hurt
What people do
They leave the maximum replica count at the default of 100 for every worker “because KEDA will handle it.”
Why it hurts
A burst or a bug can create a hundred Pods that all hit the same database. The fix for the backlog becomes a second incident.
Better approach
Calculate the highest safe concurrency for downstream systems and set the maximum with a margin below it.
What people do
They run an HPA and a ScaledObject against the same Deployment.
Why it hurts
Two controllers fight over the replica count. The number of Pods jumps up and down, and nobody can explain why.
Better approach
Delete the hand-made HPA. Put every trigger, including CPU if needed, into the ScaledObject.
What people do
They scale long, uninterruptible tasks with a Deployment, then wonder why work is lost or repeated during scale-down.
Why it hurts
The HPA terminates Pods without knowing they are mid-task. Messages come back to the queue, and half-finished work is repeated or corrupted.
Better approach
Use ScaledJob, or make workers handle the stop signal and finish their current message with a long enough grace period.
What people do
They scale on queue length alone while workers are quietly failing and returning messages to the queue.
Why it hurts
The queue stays long, so KEDA adds more failing Pods, and cost rises without any work being finished.
Better approach
Add a dead-letter queue, alert on error rates, and cap the maximum so a failure cannot turn into a runaway bill.
26Best Practices and Common Mistakes
A short checklist you can paste into your team’s playbook.
Do
- Measure how much work one Pod can finish before setting the target value
- Set a maximum that downstream systems can handle
- Use identity-based authentication where possible
- Handle the stop signal and finish the current message
- Make consumers idempotent and add dead-letter queues
- Alert on scaler errors, fallback mode and hitting the maximum
- Keep ScaledObjects in Git, next to the workload they scale
- Load-test with a realistic burst before going live
Do not
- Create an HPA for a workload already managed by KEDA
- Scale to zero for paths where a person waits on a cold start
- Set a very short polling interval on rate-limited services
- Share one powerful credential across all teams
- Forget that the node layer must also scale down
- Treat the queue as bottomless and ignore message age
- Rely on CPU alone for workers that mostly wait
- Leave experimental features in critical paths without testing
The most common mistakes, explained
- Wrong target value. Setting the target to 1 message per Pod, “to be fast”, creates huge Pod counts for small backlogs. Set it by measured capacity.
- Ignoring the activation value. A workload that wakes for every single message may start and stop constantly. Raise the activation value when starting is costly.
- Forgetting that the cooldown only covers the last step. Scaling from many Pods to one is controlled by HPA behavior, not the cooldown period.
- Using the wrong metric type. A value that is a total needs to be divided among Pods. A value already measured per Pod should not be. Mixing them gives wrong Pod counts.
- Setting minimum replicas above zero by habit. This quietly removes the savings of scale to zero.
- Trusting default timeouts for slow sources. A scaler that often times out causes erratic scaling. Fix the source or choose a different metric.
- Not testing fallback. Teams configure it, but never block the source in a test to see what happens.
Keep a small “scaling contract” document per service: what the trigger is, why the target value was chosen, the maximum and the reason for it, and who to call. When the system behaves strangely at 3 a.m., this one page saves hours.
27Troubleshooting Guide
When scaling does not behave, work through these problems in order. Most issues fall into a handful of categories.
Problem: the workload does not scale at all
- Check the ScaledObject status. Is it ready? An invalid trigger setting or an unreachable source shows up here first.
- Check the operator log. Look for authentication errors, connection errors or messages that the target could not be found.
- Check the HPA. Does the HPA exist, and does it report a current metric value? A missing value usually means the metrics server is not registered correctly.
- Check the target. The name and kind in the ScaledObject must match the real workload in the same namespace.
Problem: it will not wake from zero
Confirm that the trigger really reports activity. Compare the measured number with the activation value. If the activation threshold is higher than the current backlog, the trigger is correctly inactive. Also check whether the pause annotation is present, and whether a minimum of zero is actually set.
Problem: it scales up but never down
Look at the stabilization window and the scale-down policies. A long window holds the highest recent recommendation. Check whether another trigger, such as a CPU trigger or a cron window, is keeping the number high. Look also for messages that are stuck in flight, which can make the queue appear non-empty.
Problem: the Pod count flaps up and down
Suspect a target value that is too sensitive, a polling interval shorter than the processing time, or two autoscalers competing for the same workload. Lengthen the stabilization window and confirm that there is only one HPA.
Problem: scaling is correct but still too slow
Measure each stage: polling delay, HPA delay, scheduling, image pull and warm-up. Most of the time the largest part is Pod and node startup, not KEDA. Reduce image size, keep spare node capacity, and shorten the polling interval where the source allows it.
Problem: errors about metrics not available
This normally means the external-metrics interface is not served properly. Check that the metrics server Pod is healthy, that its registration with the API server shows as available, and that no other adapter has claimed the same interface.
A quick mental model: three questions find most faults. Can KEDA read the source? Does the HPA see the number? Can the cluster place the new Pods? A “no” to any of them tells you which layer to fix.
28Real-World and Industry Examples
KEDA is not a niche tool. It sits underneath major cloud services and runs in many production clusters.
Microsoft: Azure Functions and Azure Container Apps
KEDA was created partly to let Azure Functions run on any Kubernetes cluster and still scale on events. Azure Container Apps, a serverless container service, uses KEDA for its scale rules, so its queue, stream and schedule rules map directly onto KEDA scalers. Azure Kubernetes Service also offers KEDA as a managed add-on.
Red Hat: OpenShift Custom Metrics Autoscaler
Red Hat co-founded KEDA and ships a supported distribution of it for OpenShift customers, called the Custom Metrics Autoscaler. Enterprises use it to scale workloads on Kafka lag and Prometheus metrics inside their regulated OpenShift environments.
E-commerce order processing
An online retailer places each order on a queue. Workers charge the card, reserve stock and send confirmation. During promotions the queue explodes, and KEDA grows the workers from a handful to several dozen within a couple of minutes. After the rush, the workers shrink back, and at night they scale to zero.
Streaming analytics
A company processes sensor and clickstream data from Kafka. Consumer lag is the signal. KEDA keeps one consumer per few partitions and grows consumers as lag grows, up to the number of partitions, since extra consumers beyond that would sit idle.
Machine learning inference and batch scoring
A data team runs scoring jobs when new data lands in storage. A ScaledJob starts one job per new batch, on GPU nodes that are only paid for while the jobs run. When no data arrives, no GPU costs accrue.
CI build runners
Engineering teams run self-hosted build agents. A scaler watches the number of pending builds in the CI system and creates agents on demand. Idle agents disappear, and developers wait less for builds during busy hours.
In every example there is a clear backlog signal, workers that can safely run in parallel, and a ceiling that protects downstream systems. The project also maintains a public list of end-user adopters if you want to see who else uses it.
29Case Study: Moving a Payment Notification Platform to KEDA
This is an illustrative story built from common patterns. The company is fictional, but the decisions and problems are realistic.
The situation
PayWave is a payments company whose platform sends receipts, fraud alerts and settlement reports through a set of queue workers. There were 45 worker services, each running at least three Pods around the clock for safety. Traffic was heavily uneven: a monthly salary day created a rush 30 times higher than a quiet night. The team faced two problems. During salary day, queues grew faster than the old CPU-based scaling reacted, and customers received receipts up to 20 minutes late. During the rest of the month, most workers sat idle and cost money.
The approach
Measure capacity
The team benchmarked each worker to learn how many messages one Pod could finish in 30 seconds. These numbers became the target values.
Pilot with three services
They moved the receipt, alert and report workers first. Receipt workers kept a minimum of two Pods because customers expect fast receipts. Report workers went to zero.
Protect the database
Load testing showed the database failed at about 60 concurrent worker connections. The combined maximum across services was set below that, and a connection pool was added.
Add schedules
A cron trigger raised the receipt workers’ floor on salary days an hour before the expected rush.
Roll out and observe
The remaining services moved over four weeks, with dashboards showing queue age, Pod count and error rates side by side.
Problems they hit
The first salary day exposed a poison message that crashed workers and returned to the queue repeatedly. The queue never emptied, so KEDA kept scaling to the maximum. The cap held the damage, and an alert on the maximum being reached caught it within minutes. The team then added a dead-letter queue. Another issue was a cold start of nearly two minutes for report workers because of a large image. Slimming the image brought it under 20 seconds.
The results
Benchmark before choosing targets, protect downstream systems with a ceiling, use schedules for predictable peaks, add dead-letter queues, and keep a minimum for services where a person is waiting. Most of the value came from these ordinary habits, not from any clever setting.
30Migrating, Upgrading and Keeping KEDA Healthy
Adopting KEDA is a project, and so is keeping it current. These steps make both smooth.
Migrating from a plain HPA
Inventory
List the workloads that scale on CPU today but really depend on queues, streams or schedules. Those are the best candidates.
Translate
Choose the trigger that reflects real work, and set the target from measured capacity. Keep a CPU trigger as a safety valve if useful.
Switch over carefully
Remove the old HPA and create the ScaledObject in the same change, so the two never fight. Keep the replica count stable during the swap.
Watch and tune
Compare queue age and cost before and after. Adjust targets, polling and stabilization.
Upgrading KEDA
Treat each upgrade like any platform change. Read the release notes for changed defaults, deprecated settings and renamed metrics. Upgrade a test cluster first and replay a burst. Check that scalers you rely on have not changed behavior. Pin versions so no one gets a surprise upgrade. Confirm that your Kubernetes version is supported by the KEDA version you choose, since each release supports a range of Kubernetes versions.
Ongoing care
- Review quarterly. Look at which workloads hit their maximum, which never scale, and which keep a high minimum without a reason.
- Retire unused objects. Delete ScaledObjects for services that no longer exist.
- Track deprecations. Scalers and settings are occasionally renamed or removed. Watch for warnings in logs.
- Rehearse failure. Once in a while, block a source in a test environment and confirm that fallback and alerts behave as designed.
An autoscaler is like the thermostat in a building: install it once and it quietly works, but someone still needs to check the settings each season, replace parts that wear out, and confirm that it behaves when the weather does something unusual.
31Frequently Asked Questions
Short answers to the questions people ask most often.
32Summary and Key Takeaways
If you remember only a handful of ideas from this guide, make them these.
Key Takeaways
- Scale on the work waiting, not on how hot the kitchen looks. For queues, streams and schedules, backlog signals predict demand far better than CPU.
- KEDA extends the HPA, it does not replace it. The operator handles zero to one, and a real HPA handles one to many through KEDA’s metrics server.
- One object tells the story. A ScaledObject names the target, the limits, and the triggers. A ScaledJob creates a Job per unit of work for long tasks.
- Activation and target are different ideas. One decides whether to be awake, and the other decides how many copies to run.
- Protect what sits behind the workers. Pick a maximum that downstream databases and partners can handle, and cap scaling speed if needed.
- Secure the autoscaler. Use workload identity, namespaced authentication, narrow permissions and policy limits on who may create scaling objects.
- Measure, then tune. Watch queue age, scaler errors, fallback and Pod counts together. Benchmark capacity to choose targets, and remember that Pod and node startup are often the slowest steps.