Deploying the Elastic Distribution of OpenTelemetry (EDOT) Collector on Kubernetes sends cluster metrics, node metrics, container logs, and application telemetry to Elastic Observability through the OpenTelemetry kube-stack Helm chart. It fits clusters where workloads already use OpenTelemetry or where the OpenTelemetry Operator will inject Elastic-supported instrumentation into selected namespaces.
The Elastic-supported Kubernetes path installs the OpenTelemetry Operator plus three Collector roles. A cluster Collector gathers cluster-level metrics and events, a daemon Collector runs on nodes for node metrics, logs, and application telemetry, and a gateway Collector exports the processed data to Elastic.
The chart expects an elastic-secret-otel secret with elastic_endpoint and elastic_api_key keys in the opentelemetry-operator-system namespace. Use the versioned values file and chart version shown by Elastic for the target deployment, and use cert-manager in production when the operator webhook certificate should renew automatically instead of relying on the chart's self-signed certificate.
$ kubectl config current-context production-observability
Installing into the wrong context creates collectors, webhooks, and cluster-wide permissions in the wrong cluster.
$ helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts --force-update "open-telemetry" has been added to your repositories
$ kubectl create namespace opentelemetry-operator-system namespace/opentelemetry-operator-system created
$ umask 077
$ cat > elastic-otel.env <<'EOF' elastic_endpoint=https://elastic.example.com:443 elastic_api_key=REDACTED_API_KEY EOF
Keep the real API key out of saved transcripts, tickets, screenshots, and shared shell history.
$ kubectl create secret generic elastic-secret-otel \ --namespace opentelemetry-operator-system \ --from-env-file=elastic-otel.env secret/elastic-secret-otel created
The rendered EDOT gateway Collector reads ELASTIC_API_KEY from elastic_api_key and ELASTIC_ENDPOINT from elastic_endpoint in this secret.
$ rm elastic-otel.env
$ helm upgrade --install opentelemetry-kube-stack open-telemetry/opentelemetry-kube-stack \ --namespace opentelemetry-operator-system \ --values https://raw.githubusercontent.com/elastic/elastic-agent/refs/tags/v9.4.2/deploy/helm/edot-collector/kube-stack/values.yaml \ --version 0.12.4 Release "opentelemetry-kube-stack" does not exist. Installing it now. NAME: opentelemetry-kube-stack NAMESPACE: opentelemetry-operator-system STATUS: deployed REVISION: 1
Replace the values URL and chart version when Elastic's Add Data screen or compatibility notes show a different pair for the target Stack version.
$ helm status opentelemetry-kube-stack --namespace opentelemetry-operator-system NAME: opentelemetry-kube-stack NAMESPACE: opentelemetry-operator-system STATUS: deployed REVISION: 1 ##### snipped #####
$ kubectl get pods --namespace opentelemetry-operator-system NAME READY STATUS RESTARTS AGE opentelemetry-kube-stack-opentelemetry-operator-6f6dbb9b7c-nx7dk 2/2 Running 0 4m opentelemetry-kube-stack-cluster-stats-collector-74dddc8f6-s5msx 1/1 Running 0 3m opentelemetry-kube-stack-daemon-collector-8jfq2 1/1 Running 0 3m opentelemetry-kube-stack-daemon-collector-zlq6p 1/1 Running 0 3m opentelemetry-kube-stack-gateway-collector-7db8dd6df5-5szbn 1/1 Running 0 3m
The daemon Collector has one pod per scheduled node, so the exact count should match the node placement in the cluster.
$ kubectl logs deployment/opentelemetry-kube-stack-gateway-collector \
--namespace opentelemetry-operator-system
2026-06-18T10:16:31.014Z info service@v0.138.0/service.go:199 Starting otelcol
2026-06-18T10:16:31.275Z info otlpreceiver@v0.138.0/otlp.go:116 Starting GRPC server {"endpoint": "0.0.0.0:4317"}
2026-06-18T10:16:31.276Z info otlpreceiver@v0.138.0/otlp.go:173 Starting HTTP server {"endpoint": "0.0.0.0:4318"}
2026-06-18T10:16:31.318Z info service@v0.138.0/service.go:225 Everything is ready. Begin running and processing data.
Authentication or endpoint errors usually appear in the gateway Collector because that role exports data to Elastic.
$ kubectl annotate namespace apps instrumentation.opentelemetry.io/inject-nodejs="opentelemetry-operator-system/elastic-instrumentation" --overwrite namespace/apps annotated
Use nodejs, java, python, dotnet, or go as supported by the workload, then restart the application pods so the instrumentation is injected.
The dashboard should show the cluster, nodes, namespaces, pods, and recent metrics from the deployed Collectors.