﻿---
title: Deploy Elastic OpenTelemetry for Kubernetes observability
description: Instructions for deploying {{edot}} components for Kubernetes monitoring, using guided onboarding or manual steps.
url: https://www.elastic.co/elastic/docs-builder/docs/4850/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/deployment
products:
  - Elastic Agent
  - Elastic Cloud Hosted
  - Elastic Cloud Serverless
  - Elastic Documentation
  - Elastic Observability
applies_to:
  - Serverless Observability projects: Generally available
  - Elastic Stack: Generally available
  - Elastic Agent: Generally available
---

# Deploy Elastic OpenTelemetry for Kubernetes observability
You can use the [guided onboarding](#deploy-using-the-guided-onboarding) or [deploy all components manually](#manual-deployment).
The deployment uses different Helm values files depending on where Elasticsearch is hosted. For Elastic Cloud Serverless and Elastic Cloud Hosted, the recommended path uses the [Managed OTLP endpoint](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4850/reference/managed-inputs/managed-otlp-endpoint), which handles data enrichment server-side. For self-managed Elastic Stack, Elastic Cloud Enterprise (ECE), and Elastic Cloud on Kubernetes (ECK), data is sent directly to Elasticsearch using the `elasticsearch` exporter.

## Deploy using the guided onboarding

The guided onboarding simplifies deploying your Kubernetes components by setting up an API key and the needed integrations in the background.
Follow these steps to use the guided onboarding:
1. In Kibana, navigate to **Observability** → **Add data**.
2. Select **Kubernetes**, then choose **Kubernetes monitoring with Elastic Agent**.
3. Follow the instructions to install the OpenTelemetry Operator using the Helm chart and the provided `values.yaml`.

When installing the OpenTelemetry Operator:
<applies-switch>
  <applies-item title="serverless:" applies-to="Elastic Cloud Serverless: Generally available">
    - Make sure the `elastic_otlp_endpoint` shown in the installation command is valid for your Elastic Cloud Serverless project. This is a Managed OTLP endpoint URL, not an Elasticsearch URL.
    - The `elastic_api_key` shown in the installation command corresponds to an API key created by Kibana when the onboarding process is initiated.
  </applies-item>

  <applies-item title="ech:" applies-to="Elastic Cloud Hosted: Generally available">
    - Make sure the `elastic_otlp_endpoint` shown in the installation command is valid for your Elastic Cloud Hosted deployment. This is a Managed OTLP endpoint URL, not an Elasticsearch URL.
    - The `elastic_api_key` shown in the installation command corresponds to an API key created by Kibana when the onboarding process is initiated.
  </applies-item>

  <applies-item title="{ self:, ece:, eck: }" applies-to="Elastic Cloud on Kubernetes: Generally available">
    - Make sure the `elastic_endpoint` shown in the installation command is valid for your environment. If not, replace it with the correct Elasticsearch endpoint.
    - The `elastic_api_key` shown in the installation command corresponds to an API key created by Kibana when the onboarding process is initiated.
  </applies-item>
</applies-switch>

<note>
  The default installation deploys an OpenTelemetry Operator with a self-signed TLS certificate.
  To automatically generate and renew certificates, refer to [cert-manager integrated installation](/elastic/docs-builder/docs/4850/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/customization#cert-manager-integrated-installation) for instructions on customizing the `values.yaml` file before running the `helm install` command.
</note>


## Manual deployment

Follow these steps for a manual deployment of all components.

### Elastic Stack preparations

Before installing the operator, retrieve your credentials and install the required integrations.
<applies-switch>
  <applies-item title="serverless:" applies-to="Elastic Cloud Serverless: Generally available">
    Follow these steps to retrieve the managed OTLP endpoint URL for your Serverless project:
    1. In Elastic Cloud Serverless, open your Observability project.
    2. Go to **Add data** → **Application** → **OpenTelemetry**.
    3. Select **Managed OTLP Endpoint** in the second step.
    4. Copy the OTLP endpoint configuration value.
    5. Select **Create API Key** to generate an API key.
  </applies-item>

  <applies-item title="ech:" applies-to="Elastic Cloud Hosted: Generally available">
    **Find your endpoint**
    The easiest way to get your endpoint and API key is from the **Add data** screen in Kibana:
    1. Go to **Add data**.
    2. In the **Connect directly to the endpoint** section, select the **OpenTelemetry** tab.
    3. Copy the **Endpoint** value.
    4. Click **Create key** to generate an API key with the required privileges.

    Alternatively, retrieve the endpoint from the Elastic Cloud Console and create an API key manually:
    1. Log in to the [Elastic Cloud Console](https://cloud.elastic.co/).
    2. Find your deployment in **Hosted deployments**, and select **Manage**.
    3. Under **Application endpoints, cluster and component IDs**, select **Managed OTLP**.
    4. Copy the **Direct endpoint** value from the flyout.

    **Create an API key**
    <note>
      The Elastic Cloud Managed OTLP Endpoint validates API keys using APM application privileges. Index-level privilege scoping is not yet supported, meaning that API keys with custom index-level role descriptors return a `PermissionDenied` error.
    </note>

    <dropdown title="Using Kibana">
      1. Go to **Stack Management** → **API keys**.
      2. Click **Create API key**, enter a name, and enable **Control security privileges**.
      3. In the role descriptors box, enter the following privileges:
         ```json
         {
           "otlp_writer": {
             "applications": [
               {
                 "application": "apm",
                 "resources": ["*"],
                 "privileges": ["event:write"]
               }
             ]
           }
         }
         ```
      4. Click **Create API key** and copy the encoded value.
    </dropdown>

    <dropdown title="Using the Elasticsearch API">
      Use the [Create API key](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-security-create-api-key) API:
      ```json

      {
        "name": "otlp-writer",
        "role_descriptors": {
          "otlp_writer": {
            "applications": [
              {
                "application": "apm",
                "resources": ["*"],
                "privileges": ["event:write"]
              }
            ]
          }
        }
      }
      ```
      The `event:write` privilege for the `apm` application is the minimum required to send data through the Elastic Cloud Managed OTLP Endpoint.
    </dropdown>
  </applies-item>

  <applies-item title="{ self:, ece:, eck: }" applies-to="Elastic Cloud on Kubernetes: Generally available">
    Create an [API Key](https://www.elastic.co/elastic/docs-builder/docs/4850/deploy-manage/api-keys/elasticsearch-api-keys).
  </applies-item>
</applies-switch>

Install the **[Kubernetes OpenTelemetry Assets](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4850/reference/kubernetes_otel)** and **[System OpenTelemetry Assets](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4850/reference/system_otel)** integrations in Kibana.
When using the [Kibana onboarding UX](#deploy-using-the-guided-onboarding), the previous actions are automatically handled by Kibana.

### Operator installation

Follow these steps to install the operator:
1. Create the `opentelemetry-operator-system` Kubernetes namespace:
   ```bash
   kubectl create namespace opentelemetry-operator-system
   ```

Create a secret with your credentials and install the Helm chart for your deployment type:
<applies-switch>
  <applies-item title="serverless:" applies-to="Elastic Cloud Serverless: Generally available">
    Replace `<ELASTIC_OTLP_ENDPOINT>` and `<ELASTIC_API_KEY>` in the following command to create a secret with your credentials.
    ```bash
    kubectl create secret generic elastic-secret-otel \
    --namespace opentelemetry-operator-system \
    --from-literal=elastic_otlp_endpoint='<ELASTIC_OTLP_ENDPOINT>' \
    --from-literal=elastic_api_key='<ELASTIC_API_KEY>'
    ```
    Install the OpenTelemetry Operator using the `kube-stack` Helm chart with the `managed_otlp` values file:
    ```bash
    helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
    helm repo update
    helm upgrade --install --namespace opentelemetry-operator-system opentelemetry-kube-stack open-telemetry/opentelemetry-kube-stack \
    --values 'https://raw.githubusercontent.com/elastic/elastic-agent/refs/tags/v9.5.6/deploy/helm/edot-collector/kube-stack/managed_otlp/values.yaml' \
    --version '0.16.0'
    ```
    For details about the pipelines, refer to [Managed OTLP Endpoint](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4850/reference/edot-collector/config/default-config-k8s#managed-otlp-endpoint).
  </applies-item>

  <applies-item title="ech:" applies-to="Elastic Cloud Hosted: Generally available">
    Replace `<ELASTIC_OTLP_ENDPOINT>` and `<ELASTIC_API_KEY>` in the following command to create a secret with your credentials.
    ```bash
    kubectl create secret generic elastic-secret-otel \
    --namespace opentelemetry-operator-system \
    --from-literal=elastic_otlp_endpoint='<ELASTIC_OTLP_ENDPOINT>' \
    --from-literal=elastic_api_key='<ELASTIC_API_KEY>'
    ```

    <note>
      On Windows PowerShell, replace backslashes (`\`) with backticks (```) for line continuation and single quotes (`'`) with double quotes (`"`).
    </note>
    Install the OpenTelemetry Operator using the `kube-stack` Helm chart with the `managed_otlp` values file:
    ```bash
    helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
    helm repo update
    helm upgrade --install --namespace opentelemetry-operator-system opentelemetry-kube-stack open-telemetry/opentelemetry-kube-stack \
    --values 'https://raw.githubusercontent.com/elastic/elastic-agent/refs/tags/v9.5.6/deploy/helm/edot-collector/kube-stack/managed_otlp/values.yaml' \
    --version '0.16.0'
    ```
    For details about the pipelines, refer to [Managed OTLP Endpoint](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4850/reference/edot-collector/config/default-config-k8s#managed-otlp-endpoint).
  </applies-item>

  <applies-item title="{ self:, ece:, eck: }" applies-to="Elastic Cloud on Kubernetes: Generally available">
    Create a secret in the new namespace with the following command:
    ```bash
    kubectl create -n opentelemetry-operator-system secret generic elastic-secret-otel \
      --from-literal=elastic_endpoint='YOUR_ELASTICSEARCH_ENDPOINT' \
      --from-literal=elastic_api_key='YOUR_ELASTICSEARCH_API_KEY'
    ```
    Replace:
    - `YOUR_ELASTICSEARCH_ENDPOINT`: Elasticsearch endpoint (**with `https://` prefix**). For example: `https://1234567.us-west2.gcp.elastic-cloud.com:443`.
    - `YOUR_ELASTICSEARCH_API_KEY`: Elasticsearch API Key created in the previous step.
    Run the following commands to deploy the `opentelemetry-kube-stack` Helm chart:
    ```bash
    helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
    helm repo update
    helm upgrade --install --namespace opentelemetry-operator-system opentelemetry-kube-stack open-telemetry/opentelemetry-kube-stack \
          --values 'https://raw.githubusercontent.com/elastic/elastic-agent/refs/tags/v9.5.6/deploy/helm/edot-collector/kube-stack/values.yaml' \
          --version 0.16.0
    ```
    This configuration includes the `elasticapm` connector and processor, which handle APM trace aggregations locally. When using the Managed OTLP endpoint, this processing happens server-side instead. For details about the pipelines, refer to [Direct ingestion into Elasticsearch](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4850/reference/edot-collector/config/default-config-k8s#direct-ingestion-into-elasticsearch).
  </applies-item>
</applies-switch>

If you need to [customize the configuration](https://www.elastic.co/elastic/docs-builder/docs/4850/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/customization), copy the values file you used and adapt it to your needs. Refer to the [compatibility matrix](/elastic/docs-builder/docs/4850/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/prerequisites-compatibility#compatibility-matrix) for a complete list of available manifests in the `release branches`.

### Deploy on OpenShift

<applies-to>
  - Elastic Agent: Generally available since 9.5
</applies-to>

On OpenShift, follow the [operator installation instructions](#operator-installation) and in step 4 use the following command to apply the OpenShift values file after the base values file:
```bash
helm upgrade --install --namespace opentelemetry-operator-system opentelemetry-kube-stack open-telemetry/opentelemetry-kube-stack \
      --values 'https://raw.githubusercontent.com/elastic/elastic-agent/refs/tags/v9.5.6/deploy/helm/edot-collector/kube-stack/values.yaml' \
      --values 'https://raw.githubusercontent.com/elastic/elastic-agent/refs/tags/v9.5.6/deploy/helm/edot-collector/kube-stack/openshift/values.yaml' \
      --version 0.16.0
```

The OpenShift values file configures the minimum permissions required to run the chart and collect Kubernetes telemetry. It creates a custom [security context constraint (SCC)](https://docs.redhat.com/en/documentation/openshift_container_platform/4.22/html/authentication_and_authorization/managing-pod-security-policies) for the daemon collector. Refer to the [OpenShift values file](https://raw.githubusercontent.com/elastic/elastic-agent/refs/tags/v9.5.6/deploy/helm/edot-collector/kube-stack/openshift/values.yaml) for the complete configuration.

#### Run the daemon collector as non-root

By default, the daemon collector runs as root to read host files. To run it as a non-root user, also apply the rootless values file after the OpenShift values file:
```bash
--values 'https://raw.githubusercontent.com/elastic/elastic-agent/refs/tags/v9.5.6/deploy/helm/edot-collector/kube-stack/openshift/rootless-values.yaml'
```

The rootless configuration uses an init container to make `/var/lib/otelcol` writable so that the collector can persist file log checkpoints.

#### Use an existing OpenTelemetry Operator

If the OpenTelemetry Operator is already installed through Operator Lifecycle Manager (OLM), such as from OperatorHub, add the following flags to prevent the chart from installing another operator and its custom resource definitions (CRDs):
```bash
--set crds.installOtel=false \
--set opentelemetry-operator.enabled=false
```


## Verify the installation

Perform the following checks to verify that everything is running properly:

### Check Pods status

Ensure the following components are running without errors:
- Operator Pod
- DaemonSet Collector Pod
- Deployment Collector Pod


### Validate instrumentation object

Confirm that the Instrumentation object is deployed and configured with a valid endpoint.

### Kibana dashboard check

Verify that the **OTEL][Metrics Kubernetes] Cluster Overview** dashboard in Kibana is displaying data correctly.

### Log data availability in Kibana

In **Discover**, confirm the availability of log data. In ES|QL mode, run `FROM logs-*`. In classic mode, select the `logs-*` data view.

### Metrics data availability in Kibana

In **Discover**, ensure metrics data is available. In ES|QL mode, run `FROM metrics-*`. In classic mode, select the `metrics-*` data view.