# Scale a workload from an OTel metric

Scale a non-GPU Spring Boot service from an OTLP-push metric. This walkthrough uses the existing [OTel pipeline sample](https://github.com/kedify/examples/tree/68ab1b012dd335db4eba70db354f2c6c62db157b/samples/otel-scaler-pipelines), pinned to commit `68ab1b012dd335db4eba70db354f2c6c62db157b`. The sample routes the scaling metric to the Kedify OTel add-on while retaining a separate monitoring path.

## Scope and prerequisites

Use a disposable local environment with Docker, k3d, Helm, kubectl, curl, jq, envsubst and `hey`. The setup script creates namespaces, cert-manager/trust-manager, KEDA, OTel components and sample Prometheus/Grafana. These monitoring components demonstrate fan-out; they are not required for core OTel scaling.

The pinned setup script deletes and recreates a k3d cluster named `pipelines-tls`. Inspect it first and use it only when that named cluster is disposable. It also discovers some chart versions dynamically and contains demo TLS verification relaxations. Record resolved chart/image versions and inspect those settings before reusing the recipe; the pinned source is a reproducible definition of the scenario, not a tested production bundle.

## Inspect and start the sample

```bash
git clone https://github.com/kedify/examples.git kedify-otel-example
cd kedify-otel-example
git checkout 68ab1b012dd335db4eba70db354f2c6c62db157b
cd samples/otel-scaler-pipelines
# Review setup-springboot-push.sh, certs.yaml and springboot-push-values.yaml.
bash setup-springboot-push.sh
```

The script configures the Spring Boot app to push OTLP metrics over gRPC to the router collector on port 4317. The router selects `http.server.request.duration` for the scaler path and forwards the other metrics to the sample monitoring path. This is different from scraping the application’s Prometheus endpoint.

Verify `kubectl config current-context` identifies the intended demo cluster. Check deployments in `app`, `keda` and `observability`, and certificates with `kubectl get certificates -A`. Resolve failed readiness or certificate provisioning before load testing.

## Check the query and target

The pinned `so-springboot-push.yaml` uses an external scaler at `keda-otel-scaler.keda.svc:4318`, with TLS material supplied from the demo Secret. It selects:

```text
sum(http_server_request_duration_count{http_request_method=GET,http.response_status_code=200,http_route=/ping})
```

`operationOverTime: rate` turns the counter into a request-rate signal; `targetValue: "2"` is the sample’s per-replica target. Preserve the actual exported label spelling from the sample. Port 4318 here is the scaler connection, not an instruction to send OTLP/HTTP there. See [OTel reference](https://docs.kedify.io/reference/otel-scaler/) for the separate query/endpoint contracts.

The sample uses `minReplicaCount: 1`, `maxReplicaCount: 3`. It tests live metric scaling, not activation from zero: a metric emitted only by stopped application pods would not be a sufficient activation source.

## Send load and inspect every layer

```bash
curl --fail http://localhost:8080/ping
hey -z 120s http://localhost:8080/ping
kubectl get scaledobject spring-server-push -n app -o yaml
kubectl get hpa -n app
kubectl get pods -n app -o wide
kubectl get events -n app --sort-by=.lastTimestamp
```

The router collector’s demo file exporter records selected scaler-path samples in `/var/log/collector/keda.json`. Inspect that file/log path inside the collector with authorized pod access and confirm the metric, labels and timestamps. Compare with the sample dashboard’s source rate and HPA current metric. Expect positive load to increase desired replicas, ready Spring Boot pods to follow, and requests to complete successfully. If ingestion is absent, inspect exporter/collector TLS and routing first; if the source exists but the query is empty, inspect metric names and labels.

Stop the load and observe the sample’s 60-second scale-down stabilization and eventual return toward one replica. A fixed replica increase is not guaranteed for every laptop: it depends on achieved request rate and readiness. Use [workload diagnostics](https://docs.kedify.io/troubleshooting/workload-scaling/) at the first failing layer.

## Cleanup and next choice

Stop local port-forward/load processes. After confirming that `pipelines-tls` is the disposable cluster created for this tutorial, remove it with `k3d cluster delete pipelines-tls`. This removes its demo workloads and data; do not use the cleanup command for a shared cluster.

For an existing collector, use [Choose an OTel ingestion method](https://docs.kedify.io/how-to/otel-scaler-integrations/) and [OTel metric scaling](https://docs.kedify.io/scalers/otel-scaler/) to adapt the signal path. Keep existing production monitoring until the replacement or fan-out path is verified.

---
Canonical: https://docs.kedify.io/how-to/scale-with-otel/
Source: src/content/docs/how-to/scale-with-otel.md
Documentation index: https://docs.kedify.io/llms.txt
