Taking KEDA to production: a layered architecture of KEDA, Karpenter, and HPA behavior, GitOps with ArgoCD or Flux for CRDs, operator monitoring, and upgrade lifecycle, multi-cluster, and SLAs for event-driven workloads.

In the previous episode, 20, we closed out our understanding of releases: v2.20.2 is stable as of July 2026, and the v3 roadmap is already in sight. But the newest version alone isn't enough — production is a different story from a playground. Episode 21 brings it all together: a production-ready architecture, GitOps for CRDs, monitoring, and lifecycle.
The term "production-ready" is often dismissed as "just use the official Helm chart". In reality that's only the first step. This episode unpacks the three layers that really matter: how KEDA interacts with HPA behavior and Karpenter, how CRDs live in a GitOps flow without making upgrades messy, and how to measure SLAs for event-driven workloads.
Remember episodes 2 and 4: KEDA doesn't replace HPA, it's the puppeteer behind it. The KEDA operator creates and manages the HPA from each ScaledObject. In production, this is the advantage — you leverage the battle-tested HPA mechanism with KEDA's event-driven trigger intelligence.
What's often missed: HPA's up-and-down behavior can be set directly from the ScaledObject. For queue workloads, scale-up should be aggressive and scale-down should be careful:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: sqs-consumer
spec:
scaleTargetRef:
name: sqs-consumer
minReplicaCount: 1
maxReplicaCount: 50
pollingInterval: 15
cooldownPeriod: 120
triggers:
- type: aws-sqs-queue
authenticationRef:
name: keda-trigger-auth-aws-credentials
metadata:
queueURL: https://sqs.ap-southeast-1.amazonaws.com/123456789012/orders
queueLength: "5"
awsRegion: ap-southeast-1
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 20
periodSeconds: 60stabilizationWindowSeconds: 0 on scaleUp makes the response to queue spikes instant, while 300 seconds on scaleDown prevents replicas from dropping before the queue is truly empty — two numbers that directly affect cost and availability (episode 15).
KEDA raises pod replicas from 0 to 50 — but those pods have to wait for available nodes. That's where Karpenter (episode 16) comes in: reading pending pods and provisioning nodes in seconds. The KEDA plus Karpenter combination is the classic layered scaling example: the application layer is raised by KEDA, the infrastructure layer by Karpenter. For cost optimization, enable spot capacity and let consolidation run when load drops.
Verify results as usual: kubectl get hpa -n production to see the current target, and kubectl get nodes -l karpenter.sh/nodepool to see the nodes Karpenter provisioned.
KEDA's CustomResourceDefinitions (CRDs) are among the most upgrade-order-sensitive resources: if a CRD is changed or removed before the operator understands its version, ScaledObjects can stop being reconciled. That's why in production, CRDs and the operator chart must not be managed manually — both live in Git.
With ArgoCD, the common pattern is App of Apps: one Application for the CRDs and one for the operator, synchronized in order via sync waves. With Flux, a single Kustomization is enough. The principle is the same as learned in episode 3: kubectl apply -f for experiments, Git as the source of truth for production.
argocd app sync keda-crds --async
argocd app sync keda --asyncFor Flux: flux reconcile kustomization keda-crds then flux reconcile kustomization keda. The golden rule: CRDs first, the operator follows — without this rule, a KEDA upgrade in production is a lottery.
Don't wait for errors to surface from users. Monitor the sync and health status of every CRD: kubectl get scaledobject,scaledjob -A shows whether everything is Ready and Active. This is exactly the episode 18 troubleshooting pattern, but now as a daily habit.
The KEDA operator and metrics server expose Prometheus metrics prefixed keda_scaler_. At least three must be on your dashboard: keda_scaler_metrics_value (metric value per scaler), keda_scaler_errors_total (errors while fetching metrics), and keda_scaler_metrics_latency (how slow the provider responds).
Operator logs are also a primary diagnosis source: kubectl logs -n keda deploy/keda-operator --tail=50.
sum(rate(keda_scaler_errors_total[5m])) by (namespace, scaledobject) > 0.1Helm upgrades are routine operations that still need a procedure: update the repo, then upgrade the chart:
helm repo update kedacore
helm upgrade keda kedacore/keda --namespace keda --reuse-values
helm list -n keda
kubectl get pods -n keda -wUse --reuse-values only if you know exactly what values have been set; when in doubt, evaluate the diff first with helm diff upgrade keda kedacore/keda -n keda (the diff plugin). Always have a rollback plan ready: helm rollback keda 38 (replace 38 with the previous revision).
A scaler isn't just code — it's also a contract. Before a major upgrade, read the changelog for each scaler you use: parameters can change, provider APIs can be deprecated. Build a version matrix: KEDA version, chart version, and the list of scalers with their provider SDK versions. Keep this matrix in Git alongside the configuration, not in your head.
KEDA is a per-cluster controller — there's no "central" mode. For many clusters, apply a consistent pattern: one Git repo with environments (staging, production), one identical set of CRDs in all clusters, and observability aggregated through a single Prometheus or Thanos. This consistency is what makes multi-cluster feel like a single platform.
SLAs for event-driven workloads are rarely measured by uptime alone — what's more relevant is responsiveness to events. A sensible SLO example: 99% of scale-ups complete within 60 seconds of an event entering the queue, and zero events lost due to autoscaling. Measure it via keda_scaler_metrics_latency, pod ready time, and queue backlog.
If this SLO is threatened, you've already learned the fix priorities: lower pollingInterval (episode 19), make sure fallback is in place (episode 11), and keep a minimum replica for critical paths (episode 15). Good autoscaling is predictable autoscaling.
This episode connects all the production elements: the three-layer architecture of KEDA, Karpenter, and HPA behavior, GitOps with ArgoCD or Flux under the CRDs-first rule, operator monitoring through keda_scaler_* metrics, upgrade and versioning lifecycle, and SLAs measured by event response latency.
Points you should take away:
keda_scaler_errors_total and latency, not just pod status.This is the second episode of the production phase. Episode 22 will close the entire series: a comparison of the autoscaling ecosystem (pure HPA, Prometheus adapter, KEDA, Knative), a recap of all 23 episodes, and the direction of KEDA v3. See you there!