Autoscaling a .NET API sounds simple:
Traffic increases
↓
More CPU
↓
More Pods
But CPU is not always the best signal for application demand.
Consider an API that accepts requests and places work onto a message queue:
Client
↓
ASP.NET Core API
↓
Message Queue
↓
Background Worker
↓
Database
The API may have relatively low CPU utilization while the queue is growing rapidly.
A CPU-based Horizontal Pod Autoscaler (HPA) may therefore have little reason to add replicas.
This is where event-driven autoscaling becomes useful.
KEDA, or Kubernetes Event-driven Autoscaling, integrates external event sources with Kubernetes autoscaling. KEDA can monitor event sources and feed metrics to the Kubernetes HPA; it also handles activation and deactivation around zero replicas.
This article compares a native Kubernetes HPA approach with KEDA for an event-driven .NET workload and develops a benchmark methodology based on:
Scaling latency
Queue backlog
Request latency
Throughput
Replica count
CPU and memory
Scale-down behavior
Resource consumption
The goal is not to claim that KEDA is universally faster.
The goal is to determine which scaling signal produces better behavior for a particular workload.
HPA and KEDA Solve Different Problems
Kubernetes HPA automatically adjusts the replica count of a scalable workload according to configured metrics. The autoscaling/v2 API supports resource, custom, and external metrics.
A traditional HPA might look like:
CPU utilization
↓
HPA
↓
Deployment replicas
KEDA introduces an event-aware layer:
Queue / Event Source
↓
KEDA
↓
HPA
↓
Deployment replicas
KEDA's current architecture uses its operator for 0-to-1 and 1-to-0 activation/deactivation, while the Kubernetes HPA handles 1-to-N scaling based on metrics exposed by KEDA.
Therefore, a fair comparison is:
| Approach | Primary Scaling Signal | Scale to Zero | Best Fit |
|---|---|---|---|
| HPA | CPU, memory, custom/external metrics | Possible in specific configurations | Resource-driven workloads |
| KEDA | Event-source metrics | Yes, for supported ScaledObject scenarios | Event-driven workloads |
| HPA + custom metrics | Application/event metric | Depends on configuration | Custom application demand |
| KEDA + HPA | Event metric exposed through KEDA | Yes | Queue/event workloads |
The Benchmark Workload
Use a simple .NET architecture:
┌──────────────┐
│ Load Generator│
└──────┬───────┘
↓
┌──────────────┐
│ ASP.NET Core │
│ API │
└──────┬───────┘
↓
┌──────────────┐
│ Message Queue│
└──────┬───────┘
↓
┌──────────────┐
│ .NET Worker │
└──────┬───────┘
↓
┌──────────────┐
│ PostgreSQL │
└──────────────┘
For the benchmark, the worker is the important component.
The worker consumes messages from the queue and performs a controlled amount of processing.
This makes queue depth a meaningful scaling signal.
Why CPU Alone Can Be Misleading
Suppose the queue contains:
10,000 messages
but the worker is waiting on I/O.
CPU might remain relatively low:
CPU = 25%
Queue = 10,000
An HPA configured for:
CPU target = 70%
may not scale.
KEDA can instead use the queue depth as the scaling signal.
Conceptually:
Queue:
10,000 messages
↓
KEDA
↓
Desired replicas increase
↓
More workers
↓
Queue drains faster
This is the central hypothesis of the benchmark.
Build the .NET Worker
A simplified worker could use the .NET BackgroundService abstraction:
public sealed class OrderWorker(
IMessageConsumer consumer,
IOrderProcessor processor)
: BackgroundService
{
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
await foreach (
var message in consumer.ReadAsync(
stoppingToken))
{
await processor.ProcessAsync(
message,
stoppingToken);
}
}
}
The production implementation depends on the selected queue technology.
The benchmark should keep the worker implementation identical between HPA and KEDA experiments.
Only the scaling mechanism should change.
Add Controlled Processing Time
For a reproducible experiment, the worker can perform deterministic work.
For example:
public sealed class OrderProcessor
{
public async Task ProcessAsync(
OrderMessage message,
CancellationToken cancellationToken)
{
await Task.Delay(
TimeSpan.FromMilliseconds(100),
cancellationToken);
// Perform deterministic test work.
}
}
This is a benchmark mechanism, not a recommendation for production processing.
The purpose is to create a controllable service time.
Define the HPA Configuration
A resource-based HPA can target CPU utilization:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-worker
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-worker
minReplicas: 1
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
The autoscaling/v2 API supports configurable scaling behavior and multiple metrics. Kubernetes calculates the desired replica count from the relationship between the observed metric and configured target.
Resource Requests Matter
CPU utilization-based HPA depends on CPU resource requests.
For example:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
If resource requests are missing, CPU utilization may not be defined for the relevant HPA calculation. Kubernetes explicitly documents this behavior.
Therefore, resource requests must be part of the benchmark configuration.
Define the KEDA ScaledObject
KEDA uses a ScaledObject to connect a workload to an event source.
A simplified structure is:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: order-worker
spec:
scaleTargetRef:
name: order-worker
minReplicaCount: 0
maxReplicaCount: 20
pollingInterval: 5
triggers:
- type: rabbitmq
metadata:
queueName: orders
mode: QueueLength
value: "10"
The exact trigger metadata depends on the event source and KEDA scaler being used.
KEDA's ScaledObject defines the target workload, replica bounds, polling behavior, and triggers. The current KEDA documentation describes pollingInterval as the interval used to check trigger sources, with a default of 30 seconds.
For benchmarking, explicitly configure the interval instead of relying on an implicit default.
KEDA's Two Scaling Phases
Understanding KEDA's scaling model is essential.
There are effectively two phases:
0 ↔ 1 replicas
↓
KEDA activation
1 ↔ N replicas
↓
Kubernetes HPA
KEDA handles activation around zero replicas, while HPA handles scaling after the workload is active.
This makes KEDA particularly interesting for workloads that can remain idle.
Benchmark Question #1: How Fast Does Scaling Begin?
The first metric should be:
Scale-up latency
Define:
T0 = load spike begins
T1 = scaling signal becomes active
T2 = new Pod starts
T3 = new Pod becomes Ready
T4 = new Pod processes useful work
Then measure:
T4 - T0
This is more useful than measuring only:
HPA updated replicas
because a desired replica count does not mean the new Pod is ready to process requests.
Benchmark Question #2: How Fast Does the Queue Drain?
Record:
Initial queue depth
Peak queue depth
Queue depth over time
Time to return below threshold
For example:
Queue Depth
│
│ █
│ █ █
│ █ █
│ █ █
│____█_______█________
Time
The important measurement is not simply the maximum number of replicas.
It is how effectively the system responds to backlog.
Benchmark Question #3: Does CPU HPA React Too Late?
Consider:
Queue:
0 → 10,000
CPU:
30% → 40%
A CPU-based HPA may not scale aggressively.
KEDA sees:
Queue:
10,000
and can use that event metric directly.
The experiment should determine whether this translates into:
Lower queue latency
+
Faster backlog recovery
without causing excessive resource usage.
Benchmark Question #4: Does KEDA Over-Scale?
Event metrics can also create aggressive scaling.
Suppose:
Queue = 10,000
and:
Target = 10 messages/replica
The autoscaler can request a large number of replicas, bounded by the configured maximum.
KEDA's documentation explains that trigger metrics ultimately feed HPA calculations, with AverageValue and Value metric types producing different replica calculations.
Therefore, measure:
Desired replicas
Actual replicas
Queue depth
Processing throughput
together.
Benchmark Question #5: What Happens When the Load Stops?
Scale-down behavior is equally important.
Create:
Load spike
↓
Queue grows
↓
Workers scale out
↓
Load stops
↓
Queue drains
↓
Workers scale down
Measure:
Last message processed
↓
Replica reduction
↓
Minimum replica count
Kubernetes HPA has configurable scale-down behavior and stabilization windows. The documented default scale-down stabilization window is 300 seconds when no custom behavior is specified.
KEDA can pass HPA behavior configuration through its ScaledObject.
Benchmark Question #6: Does Scale-to-Zero Save Resources?
For an intermittent workload, compare:
HPA:
minReplicas = 1
with:
KEDA:
minReplicaCount = 0
Measure:
Idle CPU
Idle memory
Replica-hours
Startup latency
First-message latency
Do not claim a universal cost saving.
The actual result depends on:
Pod resource requests
Cluster capacity
Workload frequency
Cloud pricing
Startup time
Minimum replicas
Benchmark the Cold Start
Scale-to-zero introduces a trade-off.
Before the event:
Replicas = 0
A new event arrives:
Event
↓
KEDA detects activity
↓
Deployment scales
↓
Pod starts
↓
Readiness passes
↓
Work begins
The first event can therefore experience additional latency.
Measure:
Event arrival
↓
First Pod ready
↓
First successful processing
This is one of the most important metrics in the KEDA benchmark.
Benchmark Warm Scaling
Then repeat the experiment with:
minReplicaCount = 1
Now there is already a running worker.
Compare:
Cold:
0 → 1
Warm:
1 → N
This separates scale-to-zero startup cost from ordinary horizontal scaling.
Test Different Queue Depths
Do not test one backlog size.
Use a matrix such as:
100 messages
1,000 messages
10,000 messages
100,000 messages
The exact values should be chosen based on the capacity of the test environment.
For each run, measure:
Scale-up time
Peak replicas
Drain time
P95 processing latency
CPU
Memory
Test Different Arrival Rates
Queue depth is not the only variable.
Test:
10 messages/sec
100 messages/sec
500 messages/sec
1,000 messages/sec
The appropriate rates depend on the workload.
The important relationship is:
Arrival rate
vs
Processing rate
If:
Arrival rate > Processing rate
the queue will continue growing unless scaling increases capacity.
Calculate Required Processing Capacity
Suppose one worker processes:
10 messages/sec
and incoming traffic is:
100 messages/sec
The theoretical steady-state requirement is:
100 / 10 = 10 workers
This is not a production capacity guarantee.
It is simply a baseline for the experiment.
Real systems also need to account for:
Startup time
Failures
Retries
I/O latency
Uneven work
Queue semantics
Database capacity
Benchmark With Database Pressure
A worker often does more than consume messages.
It may execute:
Read
Transform
Write
against a database.
Therefore, add PostgreSQL or another production-relevant datastore to the benchmark.
Measure:
Worker replicas
+
Database CPU
+
Database connections
+
Query latency
An autoscaler can improve queue throughput while simultaneously overwhelming the database.
That is an autoscaling failure, not a success.
The Database Connection Trap
Suppose each .NET worker creates:
10 database connections
and autoscaling produces:
20 workers
The database may suddenly receive:
200 connections
Scaling the application does not automatically mean the database can scale at the same rate.
Therefore, include database connection pressure in the benchmark.
Benchmark Backpressure
A production system needs a maximum sustainable rate.
Consider:
Queue
↓
Worker
↓
Database
If the database becomes saturated:
Database latency ↑
↓
Worker throughput ↓
↓
Queue depth ↑
↓
Autoscaler adds workers
↓
Database pressure ↑
This is a positive feedback loop.
More replicas can make the problem worse.
Therefore, the benchmark should test the system near and beyond its sustainable capacity.
Benchmark Maximum Replica Limits
Configure:
maxReplicaCount: 20
Then create a load that requires more than 20 replicas.
Observe:
Queue continues growing
+
Replicas remain capped
This verifies that the safety boundary works.
A maximum replica count is not merely a cost-control setting.
It can protect downstream systems.
Benchmark HPA Behavior
Kubernetes HPA operates as a control loop. The default controller synchronization period is 15 seconds, although this is configurable.
Therefore, do not interpret autoscaling as instantaneous.
A useful timeline is:
Load changes
↓
Metric changes
↓
Metric becomes observable
↓
HPA evaluates
↓
Desired replicas change
↓
Scheduler places Pods
↓
Container starts
↓
Readiness passes
↓
Traffic/work distribution changes
Every stage contributes latency.
Benchmark KEDA Polling
KEDA's default pollingInterval is 30 seconds, but it can be configured per ScaledObject.
For example:
spec:
pollingInterval: 5
This does not mean the complete scaling operation will always finish within five seconds.
It means KEDA checks the trigger according to that polling configuration.
The rest of the control path still matters.
Do Not Set Polling to an Arbitrarily Low Value
Lower polling intervals can increase responsiveness, but they can also increase load on the event source and KEDA infrastructure.
Test:
5 seconds
10 seconds
30 seconds
and measure:
Scale latency
Event-source query rate
KEDA resource consumption
Choose the lowest interval that provides meaningful operational value.
Benchmark the HPA Metrics Path
For CPU-based HPA, verify that metrics are actually available.
Run:
kubectl top pods
Then inspect:
kubectl get hpa
For more detail:
kubectl describe hpa order-worker
Look for:
Current Metrics
Desired Replicas
Current Replicas
Conditions
Events
Kubernetes documents that HPA commonly obtains resource metrics through the Metrics Server and can also consume custom and external metrics through the corresponding APIs.
Inspect KEDA Metrics
KEDA exposes external metrics through its metrics API.
For example:
kubectl get --raw \
"/apis/external.metrics.k8s.io/v1beta1/namespaces/default/..."
KEDA documents this API path for inspecting external metrics associated with ScaledObject resources.
This is useful when:
Queue grows
but
replicas do not increase
You can determine whether the problem is:
Event source
↓
KEDA scaler
↓
External metric
↓
HPA
↓
Deployment
Create a Failure Scenario
A good benchmark should include failure injection.
For example:
Queue service unavailable
Then observe:
KEDA metric unavailable
HPA behavior
Fallback behavior
Replica count
Application availability
KEDA supports fallback configuration for scaler failures, allowing a defined replica behavior after a configured number of consecutive scaler failures.
Do not enable fallback blindly.
Define what behavior is safe for the particular workload.
Test Queue Recovery
Simulate:
Queue available
↓
Traffic spike
↓
KEDA scales
↓
Queue becomes unavailable
↓
Queue recovers
Measure:
Recovery detection
Replica behavior
Message processing
Error rate
A production autoscaler should not merely handle the happy path.
Test Duplicate Scalers
Do not accidentally configure:
HPA
+
KEDA ScaledObject
to independently manage the same workload.
KEDA's documentation warns against having multiple autoscaling resources compete for the same workload. Its current documentation also provides a mechanism to transfer ownership of an existing HPA to a ScaledObject when migrating to KEDA.
A clean architecture is:
One workload
↓
One scaling authority
with KEDA managing the generated HPA when KEDA is being used for that workload.
Compare the Two Configurations
HPA Configuration
Deployment
↓
CPU metric
↓
HPA
↓
Replica count
KEDA Configuration
Deployment
↓
ScaledObject
↓
Event source
↓
KEDA
↓
External metric
↓
HPA
↓
Replica count
The KEDA architecture introduces additional components, but it provides event-aware scaling capabilities.
Benchmark Matrix
Use a matrix like this:
| Test | HPA CPU | KEDA Queue |
|---|---|---|
| Idle replicas | Measure | Measure |
| 100-message burst | Measure | Measure |
| 1,000-message burst | Measure | Measure |
| Large burst | Measure | Measure |
| Sustained arrival | Measure | Measure |
| Scale-up latency | Measure | Measure |
| Queue drain time | Measure | Measure |
| P95 processing latency | Measure | Measure |
| Peak replicas | Measure | Measure |
| CPU | Measure | Measure |
| Memory | Measure | Measure |
| Scale-down time | Measure | Measure |
| Cold-start latency | N/A if min=1 | Measure |
| Event-source overhead | N/A | Measure |
This gives you a much stronger comparison than simply recording replica counts.
Example Results Template
Do not publish fabricated benchmark numbers.
Populate the table from your environment:
| Scenario | Strategy | Scale-Up Time | Peak Pods | Queue Drain | P95 Latency | CPU | Memory |
|---|---|---|---|---|---|---|---|
| Small burst | HPA | Measure | Measure | Measure | Measure | Measure | Measure |
| Small burst | KEDA | Measure | Measure | Measure | Measure | Measure | Measure |
| Large burst | HPA | Measure | Measure | Measure | Measure | Measure | Measure |
| Large burst | KEDA | Measure | Measure | Measure | Measure | Measure | Measure |
| Sustained load | HPA | Measure | Measure | Measure | Measure | Measure | Measure |
| Sustained load | KEDA | Measure | Measure | Measure | Measure | Measure | Measure |
The environment should be documented alongside the results.
How to Interpret the Results
Suppose the benchmark shows:
KEDA
Lower queue drain time
Higher peak replicas
Higher CPU consumption
That does not automatically mean KEDA is better.
It means KEDA responded more aggressively.
Whether that is desirable depends on the application's objective.
If the priority is:
Minimize queue latency
the result may be positive.
If the priority is:
Minimize compute cost
the same result may be undesirable.
Use SLOs Instead of "Fastest"
Define the objective before running the benchmark.
For example:
P95 processing latency < target
Queue age < target
Error rate < target
Maximum replicas <= limit
Then compare each strategy against those constraints.
This turns the benchmark into an engineering decision rather than a feature competition.
When HPA Is the Better Choice
Native HPA is often appropriate when workload demand correlates strongly with:
CPU
Memory
Custom application metrics
External metrics
For example:
HTTP API
↓
CPU increases with request volume
↓
HPA
Kubernetes HPA already supports resource, custom, and external metrics.
If CPU is a reliable proxy for demand, adding KEDA may provide little additional value.
When KEDA Is the Better Choice
KEDA becomes particularly attractive when demand is naturally represented by an event source:
Queue depth
Message lag
Stream backlog
External event count
KEDA maintains a large collection of built-in scalers for different event sources and also supports custom scaler integrations.
For a queue-driven worker:
Queue depth
can be much more meaningful than:
CPU utilization
When KEDA Is Not the Right Answer
Do not introduce KEDA simply because it is popular.
If your workload is:
HTTP request driven
CPU bound
Always active
native HPA may be sufficient.
Similarly, if the application requires:
Low cold-start latency
scale-to-zero may not be appropriate.
Autoscaling strategy should follow workload characteristics.
Production Considerations
Database Capacity
More workers can create more database connections and queries.
Message Ordering
Scaling consumers can affect ordering semantics.
Retry Behavior
More replicas can increase retry traffic during dependency failures.
Dead-Letter Queues
A failed scaling or processing scenario should not create uncontrolled retry loops.
Idempotency
Messages may be delivered more than once depending on the messaging system.
Startup Time
Cold-start latency directly affects scale-to-zero usefulness.
Maximum Replicas
Protect downstream dependencies with sensible limits.
Observability
Monitor the queue, replicas, processing latency, and dependencies together.
Common Mistakes
Comparing Replica Counts Only
More replicas are not automatically better.
Using CPU for an Event-Driven Workload
CPU may be a poor proxy for backlog.
Ignoring Queue Age
Queue depth alone can be misleading if messages have different processing times.
Ignoring Cold Starts
Scale-to-zero can introduce startup latency.
Setting Polling Too Aggressively
More frequent event-source polling can create unnecessary overhead.
Running HPA and KEDA Independently
Two controllers competing for the same workload can create unpredictable scaling behavior.
Ignoring Downstream Capacity
Autoscaling workers can overload the database or another dependency.
Testing Only Small Bursts
Large sustained workloads reveal different scaling behavior.
Measuring Only Average Latency
Tail latency often exposes overload conditions.
Publishing Unreproducible Numbers
Benchmark results require hardware, software, workload, and configuration details.
Troubleshooting
HPA Does Not Scale
Check:
kubectl describe hpa order-worker
Then verify:
Metrics available
CPU requests configured
Target utilization
Current utilization
Maximum replicas
Kubernetes documents that CPU utilization calculations depend on resource requests.
KEDA Does Not Scale
Check:
kubectl get scaledobject
Then:
kubectl describe scaledobject order-worker
Inspect:
Ready
Active
Fallback
Trigger
Metric
Queue Is Growing but Replicas Stay Constant
Trace the complete path:
Queue
↓
KEDA scaler
↓
External metric
↓
HPA
↓
Deployment
KEDA's metrics API can help determine whether the event metric is actually being exposed.
KEDA Scales Too Aggressively
Check:
Target metric
Polling interval
Max replicas
HPA behavior
Queue characteristics
Also inspect whether multiple triggers are producing different desired replica counts.
KEDA can use multiple triggers in a ScaledObject, with HPA ultimately selecting the highest desired replica recommendation among the metrics.
Pods Scale but Queue Does Not Drain
Investigate the downstream bottleneck:
Database
External API
CPU
Network
Message broker
More replicas do not help if every replica waits on the same constrained dependency.
Best Practices
Choose a scaling signal that represents actual workload demand.
Use CPU-based HPA when CPU is a reliable demand proxy.
Use KEDA when external events are the better signal.
Measure scale-up latency.
Measure time-to-first-useful-work.
Measure queue drain time.
Measure P95 and P99 processing latency.
Measure peak replica count.
Measure CPU and memory consumption.
Test cold and warm scaling separately.
Test sustained load as well as bursts.
Test multiple queue depths.
Test multiple arrival rates.
Configure sensible maximum replicas.
Protect downstream dependencies.
Test scaler failure behavior.
Avoid competing HPA and KEDA controllers.
Keep the application implementation identical between benchmark scenarios.
Document polling intervals and HPA behavior.
Never publish benchmark numbers without documenting the environment.
Frequently Asked Questions
Is KEDA a replacement for HPA?
Not exactly.
KEDA commonly integrates with Kubernetes HPA. KEDA handles event-source integration and activation around zero, while HPA handles scaling decisions for active workloads.
Can HPA scale based on queue length?
HPA supports custom and external metrics through Kubernetes metrics APIs. Therefore, queue-related metrics can be integrated with HPA if the required metrics infrastructure exists.
KEDA simplifies many event-source integrations by providing scalers for external systems.
Why would I use KEDA instead of CPU-based HPA?
Because CPU may not represent demand accurately.
For queue processing:
Queue depth
can be a more direct indicator of pending work than:
CPU utilization
Can KEDA scale to zero?
Yes, KEDA's activation model supports scaling workloads between zero and one replica for supported ScaledObject scenarios.
CPU and memory triggers have different limitations because metrics are unavailable when no Pods are running.
Does KEDA create an HPA?
For ScaledObject-based workload scaling, KEDA manages an HPA for the target workload and feeds it metrics.
Should I use HPA and KEDA on the same Deployment?
Do not configure them as independent controllers for the same workload.
If migrating an existing HPA to KEDA, KEDA provides an ownership-transfer mechanism documented for that scenario.
What should I benchmark first?
Start with:
Scale-up latency
Queue drain time
P95 processing latency
Peak replicas
CPU
Memory
Then add failure and recovery scenarios.
Is KEDA always faster?
No.
Scaling performance depends on:
Metric source
Polling
HPA synchronization
Pod startup
Container image size
Application startup
Cluster capacity
Workload
Measure the complete path.
Does scale-to-zero always reduce cost?
Not necessarily.
It can reduce idle compute, but cold-start latency and workload frequency may make it inappropriate for some services.
Can KEDA overload my database?
Yes.
If queue depth causes aggressive scaling, every additional worker may create additional database traffic.
Database capacity must therefore be included in the scaling design.
Conclusion
KEDA and Kubernetes HPA should not be treated as competing products where one is universally better.
They provide different mechanisms for translating workload demand into Kubernetes replica counts.
Native HPA works well when metrics such as CPU, memory, or application-level metrics represent demand accurately. Kubernetes supports configurable scaling behavior and multiple metric sources through autoscaling/v2.
KEDA becomes particularly valuable when demand originates from external event sources such as queues and streams. Its architecture combines event-source monitoring with Kubernetes HPA and adds an activation path for scaling between zero and one replica.
For a .NET event-processing workload, the comparison should look like:
Workload
↓
┌───────────────┐
│ Event / Queue │
└───────┬───────┘
↓
┌─────────────────┐
│ Scaling Signal │
└───────┬─────────┘
↓
┌──────────────────────────┐
│ │
HPA CPU KEDA
│ │
↓ ↓
CPU utilization Queue/event metric
│ │
└──────────┬───────────────┘
↓
HPA
↓
.NET Workers
↓
Dependencies
The strongest benchmark does not ask:
"Which autoscaler is faster?"
It asks:
"Which scaling strategy keeps my workload
within its SLO while using an acceptable
amount of infrastructure?"
That means measuring:
Scale-up latency
Queue drain time
P95/P99 latency
Peak replicas
CPU
Memory
Cold-start latency
Scale-down behavior
Dependency pressure
Failure recovery
Most importantly, do not optimize the autoscaler in isolation.
If adding replicas reduces queue depth but overwhelms PostgreSQL, the system has not become more resilient.
If scale-to-zero reduces idle resources but introduces unacceptable first-request latency, the configuration may not be appropriate.
The correct result is therefore workload-specific.
Benchmark the signal, the scaling loop, the application, and its dependencies together.
That is the difference between configuring autoscaling and engineering an autoscaling system.

Join the conversation! Your thoughts help the community grow.