---
title: Monitor NGINX on Kubernetes with OpenTelemetry
source: https://docs.newrelic.com/docs/opentelemetry/integrations/nginx/nginx-otel-kubernetes
---

Monitor your NGINX servers running in Kubernetes clusters to send metrics and telemetry data to New Relic.

You can choose between three collector options:

-   **NRDOT:** New Relic Distribution of OpenTelemetry
-   **OTel Collector Contrib:** Standard OpenTelemetry Collector with community-contributed components
-   **Prometheus Receiver:** For environments already running [nginx-prometheus-exporter](https://github.com/nginxinc/nginx-prometheus-exporter) as a sidecar

This Kubernetes-specific integration automatically discovers NGINX pods in your cluster and collects metrics without manual configuration for each instance. It leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) and [receivercreator](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/receivercreator) to dynamically monitor NGINX performance metrics, connection statistics, and server health across your containerized environment.

## Set up NGINX monitoring [#setup]

Choose your preferred collector and follow the steps:

### NRDOT collector

#### Before you begin [#prerequisites-nrdot]

Ensure you have:

-   Valid New Relic [license key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#ingest-license-key)
-   Enable the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module on NGINX pod that needs to be monitored
-   Add labels `app` and `role` to each NGINX pod that needs to be monitored
-   For manifest install: complete the base [Kubernetes OpenTelemetry manifest installation](https://docs.newrelic.com/docs/kubernetes-pixie/k8s-otel/install/#manifest-install)
-   For helm install: complete the base [Kubernetes OpenTelemetry helm installation](https://docs.newrelic.com/docs/kubernetes-pixie/k8s-otel/install/#helm-install)

#### Configure the NRDOT collector [#install-nrdot-collector]

Install the NRDOT collector using Kubernetes manifests or Helm.

**Manifest install**

After completing the base Kubernetes OpenTelemetry manifest installation, configure NGINX monitoring by following these steps:

1.  Update the collector image to use NRDOT collector.

    In both [`deployment.yaml`](https://github.com/newrelic/helm-charts/blob/master/charts/nr-k8s-otel-collector/examples/k8s/rendered/deployment.yaml) and [`daemonset.yaml`](https://github.com/newrelic/helm-charts/blob/master/charts/nr-k8s-otel-collector/examples/k8s/rendered/daemonset.yaml) files in your local `rendered` directory, update the image to:

    ```yaml
    image: newrelic/nrdot-collector:latest
    ```

2.  Update the [`deployment-configmap.yaml`](https://github.com/newrelic/helm-charts/blob/master/charts/nr-k8s-otel-collector/examples/k8s/rendered/deployment-configmap.yaml) for NGINX monitoring:

    Choose one of the following configuration options based on your monitoring requirements:

    **NGINX-only monitoring**

    > #### ⚠️ IMPORTANT
    >
    > This option monitors NGINX only and removes other Kubernetes metrics collection. You'll delete additional collectors later to prevent unwanted metric ingestion.

    Replace the content under [`deployment-config.yaml: |`](https://github.com/newrelic/helm-charts/blob/4177f3cd73b73a82884206afc83963b504d51111/charts/nr-k8s-otel-collector/examples/k8s/rendered/deployment-configmap.yaml#L15)  with the below NGINX-specific configuration:

    ```yaml
    extensions:
      health_check:
        endpoint: 0.0.0.0:13133
      k8s_observer:
        auth_type: serviceAccount
        observe_pods: true
        observe_nodes: true

    receivers:
      receiver_creator/nginx:
        watch_observers: [k8s_observer]
        receivers:
          nginx:
            rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy"  # Update with your labels
            config:
              endpoint: 'http://`endpoint`:<YOUR_STUB_STATUS_PORT>/<YOUR_STUB_STATUS_PATH>'  # Update port and path as needed
              metrics:
                nginx.requests:
                  enabled: true
                nginx.connections_accepted:
                  enabled: true
                nginx.connections_handled:
                  enabled: true
                nginx.connections_current:
                  enabled: true
              collection_interval: 30s
            resource_attributes:
              nginx.server.endpoint: 'http://`endpoint`:<YOUR_STUB_STATUS_PORT>/<YOUR_STUB_STATUS_PATH>'  # Update port and path as needed
              nginx.port: '<YOUR_STUB_STATUS_PORT>'  # Update to match your configuration

    processors:
      batch:
        send_batch_max_size: 1000
        timeout: 30s
        send_batch_size: 800

      memory_limiter:
        check_interval: 1s
        limit_percentage: 80
        spike_limit_percentage: 25

      resource/cluster:
        attributes:
          - key: k8s.cluster.name
            value: "<CLUSTER_NAME>"  # Replace with your cluster name
            action: insert

      transform/nginx:
        metric_statements:
          - context: resource
            statements:
              - set(attributes["nginx.display.name"], Concat([
                  "server",
                  "k8s",
                  attributes["k8s.cluster.name"],
                  attributes["k8s.namespace.name"],
                  "pod",
                  attributes["k8s.pod.name"],
                  "nginx",
                  attributes["nginx.port"]
                ], ":"))
              - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"])
      
      transform/metadata_nullify:
        metric_statements:
          - context: metric
            statements:
              - set(description, "")
              - set(unit, "")

    exporters:
      otlp_http/newrelic:
        endpoint: "<YOUR_NEWRELIC_OTLP_ENDPOINT>"
        headers:
          api-key: ${env:NR_LICENSE_KEY}

    service:
      extensions: [health_check, k8s_observer]
      pipelines:
        metrics/nginx:
          receivers: [receiver_creator/nginx]
          processors: [batch, resource/cluster, transform/nginx, transform/metadata_nullify, memory_limiter]
          exporters: [otlp_http/newrelic]
    ```

    ###### Configuration parameters

    The following table describes the key configuration parameters:

    | Parameter                       | Description                                                                                                                                                                                  |
    | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `<YOUR_STUB_STATUS_PORT>`       | Replace with your NGINX stub status port (for example 80, 8080)                                                                                                                              |
    | `<YOUR_STUB_STATUS_PATH>`       | Replace with your NGINX stub status path (for example basic_status)                                                                                                                          |
    | `<CLUSTER_NAME>`                | Replace with your Kubernetes cluster name for identification in New Relic                                                                                                                    |
    | `<YOUR_NEWRELIC_OTLP_ENDPOINT>` | Update with your region's OTLP endpoint. See [OTLP endpoint documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) |
    | `app` and `role` labels         | Pod labels used to identify NGINX pods (update the rule to match your labels)                                                                                                                |
    | `collection_interval`           | Interval in seconds to collect metrics. The default value is set to `30s`                                                                                                                    |
    | `send_batch_max_size`           | Maximum number of metrics to batch before sending. The default value is set to `1000`                                                                                                        |
    | `timeout`                       | Timeout in seconds to wait before sending batched metrics. The default value is set to `30s`                                                                                                 |

    **K8s + NGINX monitoring**

    Add the following sections to your existing `deployment-configmap.yaml`:

    Extensions to add:

    ```yaml
    extensions:
      health_check:
        endpoint: 0.0.0.0:13133
      k8s_observer:
        auth_type: serviceAccount
        observe_pods: true
        observe_nodes: true
    ```

    Receivers to add:

    ```yaml
    receivers:
      receiver_creator/nginx:
        watch_observers: [k8s_observer]
        receivers:
          nginx:
            rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy"  # Update with your labels
            config:
              endpoint: 'http://`endpoint`:<YOUR_STUB_STATUS_PORT>/<YOUR_STUB_STATUS_PATH>'  # Update port and path as needed
              metrics:
                nginx.requests:
                  enabled: true
                nginx.connections_accepted:
                  enabled: true
                nginx.connections_handled:
                  enabled: true
                nginx.connections_current:
                  enabled: true
              collection_interval: 30s
            resource_attributes:
              nginx.server.endpoint: 'http://`endpoint`:<YOUR_STUB_STATUS_PORT>/<YOUR_STUB_STATUS_PATH>'  # Update port and path as needed
              nginx.port: '<YOUR_STUB_STATUS_PORT>'  # Update to match your configuration
    ```

    Processors to add:

    ```yaml
    processors:
      resource/cluster:
        attributes:
          - key: k8s.cluster.name
            value: "<CLUSTER_NAME>"  # Replace with your cluster name
            action: insert

      transform/nginx:
        metric_statements:
          - context: resource
            statements:
              - set(attributes["nginx.display.name"], Concat([
                  "server",
                  "k8s",
                  attributes["k8s.cluster.name"],
                  attributes["k8s.namespace.name"],
                  "pod",
                  attributes["k8s.pod.name"],
                  "nginx",
                  attributes["nginx.port"]
                ], ":"))
              - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"])
      
      transform/metadata_nullify:
        metric_statements:
          - context: metric
            statements:
              - set(description, "")
              - set(unit, "")
    ```

    Service pipelines to add:

    ```yaml
    service:
      extensions: [health_check, k8s_observer]  # Add to existing extensions
      pipelines:
        metrics/nginx:
          receivers: [receiver_creator/nginx]
          processors: [batch, resource/cluster, transform/nginx, transform/metadata_nullify, memory_limiter]
          exporters: [otlphttp/newrelic]
    ```

    ###### Configuration parameters

    The following table describes the key configuration parameters:

    | Parameter                 | Description                                                                   |
    | ------------------------- | ----------------------------------------------------------------------------- |
    | `<YOUR_STUB_STATUS_PORT>` | Replace with your NGINX stub status port (for example 80, 8080)               |
    | `<YOUR_STUB_STATUS_PATH>` | Replace with your NGINX stub status path (for example basic_status)           |
    | `<CLUSTER_NAME>`          | Replace with your Kubernetes cluster name for identification in New Relic     |
    | `app` and `role` labels   | Pod labels used to identify NGINX pods (update the rule to match your labels) |
    | `collection_interval`     | Interval in seconds to collect metrics. The default value is set to `30s`     |
    | `memory_limiter`          | Processor used in existing Kubernetes configuration to limit memory usage     |

3.  Apply the updated manifests and restart the deployment.

    1.  For NGINX-only monitoring, run these commands:
        ```bash
        kubectl apply -n newrelic -R -f rendered
        kubectl delete daemonset nr-k8s-otel-collector-daemonset -n newrelic
        kubectl delete deployment nr-k8s-otel-collector-kube-state-metrics -n newrelic
        kubectl rollout restart deployment nr-k8s-otel-collector-deployment -n newrelic
        ```

    2.  For K8s + NGINX monitoring, run these commands:
        ```bash
        kubectl apply -n newrelic -R -f rendered
        kubectl rollout restart deployment nr-k8s-otel-collector-deployment -n newrelic
        ```

**Helm install**

1.  Add the following sections under [`deployment.extraConfig`](https://github.com/newrelic/helm-charts/blob/5cfdda9d64dc26c289438554a23665434d4d584b/charts/nr-k8s-otel-collector/values.yaml#L259) in your [`values.yaml`](https://github.com/newrelic/helm-charts/blob/5cfdda9d64dc26c289438554a23665434d4d584b/charts/nr-k8s-otel-collector/values.yaml):

    > #### ⚠️ IMPORTANT
    >
    > In `deployment.extraConfig`, `pipelines:` must be defined at the root level — not nested under `service:`. The helm chart template maps `extraConfig.pipelines` into `service.pipelines`, and `extraConfig.service.extensions` into `service.extensions`.

    Extensions:

    ```yaml
    extensions:
      health_check:
        endpoint: 0.0.0.0:13133
      k8s_observer:
        auth_type: serviceAccount
        observe_pods: true
        observe_nodes: true
    ```

    Receivers:

    ```yaml
    receivers:
      receiver_creator/nginx:
        watch_observers: [k8s_observer]
        receivers:
          nginx:
            rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy"  # Update with your labels
            resource_attributes:
              nginx.server.endpoint: 'http://`endpoint`:<YOUR_STUB_STATUS_PORT>/<YOUR_STUB_STATUS_PATH>'  # Update port and path as needed
              nginx.port: '<YOUR_STUB_STATUS_PORT>'  # Update to match your configuration
            config:
              endpoint: 'http://`endpoint`:<YOUR_STUB_STATUS_PORT>/<YOUR_STUB_STATUS_PATH>'  # Update port and path as needed
              metrics:
                nginx.requests:
                  enabled: true
                nginx.connections_accepted:
                  enabled: true
                nginx.connections_handled:
                  enabled: true
                nginx.connections_current:
                  enabled: true
              collection_interval: 30s
    ```

Processors:

````yaml
processors:

  resource/cluster:
    attributes:
      - key: k8s.cluster.name
        value: "<CLUSTER_NAME>"  # Replace with your cluster name
        action: insert

  transform/nginx:
    metric_statements:
      - context: resource
        statements:
          - set(attributes["nginx.display.name"], Concat([
              "server",
              "k8s",
              attributes["k8s.cluster.name"],
              attributes["k8s.namespace.name"],
              "pod",
              attributes["k8s.pod.name"],
              "nginx",
              attributes["nginx.port"]
            ], ":"))
          - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"])

  transform/metadata_nullify:
    metric_statements:
      - context: metric
        statements:
          - set(description, "")
          - set(unit, "")
```

Service extensions:
```yaml
service:
  extensions: [health_check, k8s_observer]
```

Pipelines:
```yaml
pipelines:
  metrics/nginx:
    receivers: [receiver_creator/nginx]
    processors: [batch, resource/cluster, transform/nginx, transform/metadata_nullify, memory_limiter]
    exporters: [otlphttp/newrelic]
```

#### Configuration parameters

The following table describes the key configuration parameters:

<table>
<thead>
  <tr>
    <th>Parameter</th>
    <th>Description</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td>`<YOUR_STUB_STATUS_PORT>`</td>
    <td>Replace with your NGINX stub status port (for example 80, 8080)</td>
  </tr>
  <tr>
    <td>`<YOUR_STUB_STATUS_PATH>`</td>
    <td>Replace with your NGINX stub status path (for example basic_status)</td>
  </tr>
  <tr>
    <td>`<CLUSTER_NAME>`</td>
    <td>Replace with your Kubernetes cluster name for identification in New Relic</td>
  </tr>
  <tr>
    <td>`app` and `role` labels</td>
    <td>Pod labels used to identify NGINX pods (update the rule to match your labels)</td>
  </tr>
  <tr>
    <td>`collection_interval`</td>
    <td>Interval in seconds to collect metrics. The default value is set to `30s`</td>
  </tr>
  <tr>
    <td>`send_batch_max_size`</td>
    <td>Maximum number of metrics to batch before sending. The default value is set to `1000`</td>
  </tr>
  <tr>
    <td>`timeout`</td>
    <td>Timeout in seconds to wait before sending batched metrics. The default value is set to `30s`</td>
  </tr>
  <tr>
    <td>`memory_limiter`</td>
    <td>Processor used to limit memory usage. `limit_percentage` sets the maximum memory percentage before limiting begins</td>
  </tr>
</tbody>
</table>

2. Apply the updated values to your running Helm release:

```bash
helm upgrade nr-k8s-otel-collector newrelic/nr-k8s-otel-collector \
  --namespace newrelic \
  --reuse-values \
  -f values.yaml
```


````

### OpenTelemetry Collector Contrib

#### Before you begin [#prerequisites-otel]

Ensure you have:

-   Valid New Relic [license key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#ingest-license-key)
-   Enable the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module on NGINX pod that needs to be monitored
-   Add labels `app` and `role` to each NGINX pod that needs to be monitored
-   [Helm](https://helm.sh/docs/intro/install/) installed

#### Configure the OpenTelemetry Collector [#install-otelcol-collector]

Deploy the OpenTelemetry Collector to your Kubernetes cluster using Helm. The collector will automatically discover and scrape metrics from your NGINX pods.

**Step 1: Create custom values.yaml configuration**

Download or create a custom `values.yaml` file based on the [OpenTelemetry Collector values.yaml](https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-collector/values.yaml).

Update the following sections in your `values.yaml` file:

1.  Set mode to deployment:
    ```yaml
    mode: deployment
    ```

2.  Replace the image repository:
    ```yaml
    image:
      repository: otel/opentelemetry-collector-contrib
    ```

3.  Configure cluster role:
    ```yaml
    clusterRole:
      create: true
      rules:
        - apiGroups: [""]
          resources: ["pods", "nodes", "nodes/stats", "nodes/proxy"]
          verbs: ["get", "list", "watch"]
        - apiGroups: ["apps"]
          resources: ["replicasets"]
          verbs: ["get", "list", "watch"]
    ```

4.  Configure resource limits:
    ```yaml
    resources:
      limits:
        cpu: 250m
        memory: 512Mi
    ```

5.  Replace the entire config section with NGINX monitoring configuration:

    ```yaml
       config:
         extensions:
           health_check:
             endpoint: 0.0.0.0:13133
           k8s_observer:
             auth_type: serviceAccount
             observe_pods: true
             observe_nodes: true

         receivers:
           receiver_creator/nginx:
             watch_observers: [k8s_observer]
             receivers:
               nginx:
                 rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy" # Update with your labels
                 config:
                   endpoint: 'http://`endpoint`:<YOUR_STUB_STATUS_PORT>/<YOUR_STUB_STATUS_PATH>'  # Update port and path as needed
                   metrics:
                     nginx.requests:
                       enabled: true
                     nginx.connections_accepted:
                       enabled: true
                     nginx.connections_handled:
                       enabled: true
                     nginx.connections_current:
                       enabled: true
                   collection_interval: 30s
                 resource_attributes:
                   nginx.server.endpoint: 'http://`endpoint`:<YOUR_STUB_STATUS_PORT>/<YOUR_STUB_STATUS_PATH>'  # Update port and path as needed
                   nginx.port: '<YOUR_STUB_STATUS_PORT>'  # Update to match your configuration

         processors:
           batch:
             send_batch_size: 1024
             timeout: 30s

           resource/cluster:
             attributes:
               - key: k8s.cluster.name
                 value: "<CLUSTER_NAME>"  # Replace with your cluster name
                 action: insert

           transform/nginx:
             metric_statements:
               - context: resource
                 statements:
                   - set(attributes["nginx.display.name"], Concat([
                       "server",
                       "k8s",
                       attributes["k8s.cluster.name"],
                       attributes["k8s.namespace.name"],
                       "pod",
                       attributes["k8s.pod.name"],
                       "nginx",
                       attributes["nginx.port"]
                       ], ":"))
                   - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"])

           transform/metadata_nullify:
             metric_statements:
               - context: metric
                 statements:
                   - set(description, "")
                   - set(unit, "")

         exporters:
           otlp_http/newrelic:
             endpoint: "<YOUR_NEWRELIC_OTLP_ENDPOINT>"  # Update for your region
             headers:
               api-key: "<YOUR_NEW_RELIC_LICENSE_KEY>"  # Replace with your New Relic license key

         service:
           extensions: [health_check, k8s_observer]
           pipelines:
             metrics/nginx:
               receivers: [receiver_creator/nginx]
               processors: [batch, resource/cluster, transform/nginx, transform/metadata_nullify]
               exporters: [otlp_http/newrelic]
    ```

###### Configuration parameters

The following table describes the key configuration parameters:

| Parameter                       | Description                                                                                                                                                                                  |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<YOUR_STUB_STATUS_PORT>`       | Replace with your NGINX stub status port (for example 80, 8080)                                                                                                                              |
| `<YOUR_STUB_STATUS_PATH>`       | Replace with your NGINX stub status path (for example basic_status)                                                                                                                          |
| `<CLUSTER_NAME>`                | Replace with your Kubernetes cluster name for identification in New Relic                                                                                                                    |
| `<YOUR_NEWRELIC_OTLP_ENDPOINT>` | Update with your region's OTLP endpoint. See [OTLP endpoint documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) |
| `<YOUR_NEW_RELIC_LICENSE_KEY>`  | Replace with your New Relic license key                                                                                                                                                      |
| `app` and `role` labels         | Pod labels used to identify NGINX pods (update the rule to match your labels)                                                                                                                |
| `basic_status`                  | NGINX stub status endpoint path (update if using a different path)                                                                                                                           |
| `collection_interval`           | Interval in seconds to collect metrics. The default value is set to `30s`                                                                                                                    |
| `send_batch_size`               | Number of metrics to batch before sending. The default value is set to `1024`                                                                                                                |
| `timeout`                       | Timeout in seconds to wait before sending batched metrics. The default value is set to `30s`                                                                                                 |

**Step 2: Install with Helm**

Follow the [OpenTelemetry Collector Helm chart installation guide](https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-collector/README.md#opentelemetry-collector-helm-chart) to install the collector using your custom `values.yaml` file.

Example commands:

```bash
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
helm upgrade my-opentelemetry-collector open-telemetry/opentelemetry-collector -f your-custom-values.yaml -n newrelic --create-namespace --install
```

**Step 3: Verify deployment and data collection**

1.  Verify the pods are running:

    ```bash
    kubectl get pods -n newrelic --watch
    ```

    You should see the OpenTelemetry Collector pods in a `Running` state in the `newrelic` namespace.

2.  Run an NRQL query in New Relic to verify data collection. Replace the cluster name with your actual cluster name:

    ```sql
    FROM Metric
    SELECT *
    WHERE metricName LIKE 'nginx.%'
      AND instrumentation.provider = 'opentelemetry'
      AND k8s.cluster.name = 'your-cluster-name'
    SINCE 10 minutes ago
    ```

### Prometheus Receiver

Use this approach if you already have [nginx-prometheus-exporter](https://github.com/nginxinc/nginx-prometheus-exporter) running as a sidecar in your NGINX pods, or if you're migrating from a Prometheus-based monitoring stack.

> #### 💡 TIP
>
> **Recommended:** If you don't already have a Prometheus exporter running, use the **NRDOT collector** or **OpenTelemetry Collector Contrib** tabs instead. They connect directly to the NGINX stub status endpoint without needing an additional exporter component.

#### Before you begin [#prerequisites-prometheus-k8s]

Ensure you have:

-   Valid New Relic [license key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#ingest-license-key)
-   Enable the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module on each NGINX pod that needs to be monitored
-   Add labels `app` and `role` to each NGINX pod that needs to be monitored
-   [Helm](https://helm.sh/docs/intro/install/) installed

**NRDOT collector — Helm install**

Complete the base [Kubernetes OpenTelemetry helm installation](https://docs.newrelic.com/docs/kubernetes-pixie/k8s-otel/install/#helm-install) for `nr-k8s-otel-collector` before proceeding.

1.  Add the following sections under [`deployment.configMap.extraConfig`](https://github.com/newrelic/helm-charts/blob/5cfdda9d64dc26c289438554a23665434d4d584b/charts/nr-k8s-otel-collector/values.yaml#L259) in your [`values.yaml`](https://github.com/newrelic/helm-charts/blob/master/charts/nr-k8s-otel-collector/values.yaml):

    > #### ⚠️ IMPORTANT
    >
    > In `deployment.configMap.extraConfig`, `pipelines:` must be defined at the root level — not nested under `service:`. The Helm chart template maps `extraConfig.pipelines` into `service.pipelines`, and `extraConfig.service.extensions` into `service.extensions`.

    Extensions:

    ```yaml
    extensions:
      health_check:
        endpoint: 0.0.0.0:13133
      k8s_observer:
        auth_type: serviceAccount
        observe_nodes: true
        observe_pods: true
    ```

    Receivers:

    ```yaml
    receivers:
      receiver_creator/nginx_prom:
        watch_observers:
          - k8s_observer
        receivers:
          prometheus:
            rule: type == "port" && pod.labels["app"] == "nginx" && pod.labels["role"] == "reverse-proxy" && name == "metrics"  # Update with your pod labels
            config:
              config:
                scrape_configs:
                  - job_name: nginx_prom
                    metrics_path: /metrics
                    scrape_interval: 30s
                    static_configs:
                      - targets:
                          - '`endpoint`'
            resource_attributes:
              nginx.port: "9113"  # Update if nginx-prometheus-exporter runs on a different port
              nginx.server.endpoint: http://`endpoint`/metrics
    ```

    Processors:

    ```yaml
    processors:
      resource/cluster:
        attributes:
          - action: insert
            key: k8s.cluster.name
            value: "<CLUSTER_NAME>"  # Replace with your cluster name

      metricstransform/nginx_prom:
        transforms:
          - action: update
            include: nginx_http_requests_total
            new_name: nginx.requests
          - action: update
            include: nginx_connections_accepted
            new_name: nginx.connections_accepted
          - action: update
            include: nginx_connections_handled
            new_name: nginx.connections_handled
          - action: combine
            include: ^nginx_connections_(?P<state>active|reading|writing|waiting)$
            match_type: regexp
            new_name: nginx.connections_current

      filter/nginx_prom_only:
        metrics:
          include:
            match_type: regexp
            metric_names:
              - ^nginx\..*

      transform/nginx:
        metric_statements:
          - context: resource
            statements:
              - set(attributes["nginx.display.name"], Concat([ "server", "k8s", attributes["k8s.cluster.name"], attributes["k8s.namespace.name"], "pod", attributes["k8s.pod.name"], "nginx", attributes["nginx.port"] ], ":"))
              - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"])

      transform/metadata_nullify:
        metric_statements:
          - context: metric
            statements:
              - set(description, "")
              - set(unit, "")
    ```

    Service extensions:

    ```yaml
    service:
      extensions:
        - health_check
        - k8s_observer
    ```

    Pipelines:

    ```yaml
    pipelines:
      metrics/nginx_prometheus:
        receivers:
          - receiver_creator/nginx_prom
        processors:
          - batch
          - resource/cluster
          - metricstransform/nginx_prom
          - filter/nginx_prom_only
          - transform/nginx
          - transform/metadata_nullify
          - memory_limiter
        exporters:
          - otlphttp/newrelic
    ```

    ###### Configuration parameters

    The following table describes the key configuration parameters:

    | Parameter               | Description                                                                                                          |
    | ----------------------- | -------------------------------------------------------------------------------------------------------------------- |
    | `<CLUSTER_NAME>`        | Your Kubernetes cluster name. Used for identification in New Relic.                                                  |
    | `<YOUR_LICENSE_KEY>`    | Your New Relic license key.                                                                                          |
    | `app` and `role` labels | Pod labels used to discover NGINX pods. Update the `rule` to match your pod labels.                                  |
    | `name == "metrics"`     | The named container port on `9113` that the receiver_creator watches for. Must match the port name in your pod spec. |
    | `scrape_interval`       | How often to scrape Prometheus metrics. Default: `30s`.                                                              |

2.  Apply the updated values to your running Helm release:

    ```bash
    helm upgrade nr-k8s-otel-collector newrelic/nr-k8s-otel-collector \
      --namespace newrelic \
      --reuse-values \
      -f values.yaml
    ```

**OTel Collector Contrib — Helm install**

**Step 1: Create custom values.yaml configuration**

Download or create a custom `values.yaml` file based on the [OpenTelemetry Collector values.yaml](https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-collector/values.yaml).

Update the following sections in your `values.yaml` file:

1.  Set mode to deployment:
    ```yaml
    mode: deployment
    ```

2.  Replace the image repository:
    ```yaml
    image:
      repository: otel/opentelemetry-collector-contrib
    ```

3.  Configure cluster role:
    ```yaml
    clusterRole:
      create: true
      rules:
        - apiGroups: [""]
          resources: ["pods", "nodes", "nodes/stats", "nodes/proxy"]
          verbs: ["get", "list", "watch"]
        - apiGroups: ["apps"]
          resources: ["replicasets"]
          verbs: ["get", "list", "watch"]
    ```

4.  Configure resource limits:
    ```yaml
    resources:
      limits:
        cpu: 250m
        memory: 512Mi
    ```

5.  Replace the entire config section with the Prometheus receiver configuration:

    ```yaml
    config:
      extensions:
        health_check:
          endpoint: 0.0.0.0:13133
        k8s_observer:
          auth_type: serviceAccount
          observe_pods: true
          observe_nodes: true

      receivers:
        receiver_creator/nginx_prom:
          watch_observers: [k8s_observer]
          receivers:
            prometheus:
              rule: type == "port" && pod.labels["app"] == "nginx" && pod.labels["role"] == "reverse-proxy" && name == "metrics"  # Update with your pod labels
              config:
                config:
                  scrape_configs:
                    - job_name: nginx_prom
                      metrics_path: /metrics
                      scrape_interval: 30s
                      static_configs:
                        - targets:
                            - '`endpoint`'
              resource_attributes:
                nginx.port: "9113"  # Update if nginx-prometheus-exporter runs on a different port
                nginx.server.endpoint: http://`endpoint`/metrics

      processors:
        batch:
          send_batch_size: 1024
          timeout: 30s

        resource/cluster:
          attributes:
            - key: k8s.cluster.name
              value: "<CLUSTER_NAME>"  # Replace with your cluster name
              action: insert

        metricstransform/nginx_prom:
          transforms:
            - action: update
              include: nginx_http_requests_total
              new_name: nginx.requests
            - action: update
              include: nginx_connections_accepted
              new_name: nginx.connections_accepted
            - action: update
              include: nginx_connections_handled
              new_name: nginx.connections_handled
            - action: combine
              include: ^nginx_connections_(?P<state>active|reading|writing|waiting)$
              match_type: regexp
              new_name: nginx.connections_current

        filter/nginx_prom_only:
          metrics:
            include:
              match_type: regexp
              metric_names:
                - ^nginx\..*

        transform/nginx:
          metric_statements:
            - context: resource
              statements:
                - set(attributes["nginx.display.name"], Concat([ "server", "k8s", attributes["k8s.cluster.name"], attributes["k8s.namespace.name"], "pod", attributes["k8s.pod.name"], "nginx", attributes["nginx.port"] ], ":"))
                - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"])

        transform/metadata_nullify:
          metric_statements:
            - context: metric
              statements:
                - set(description, "")
                - set(unit, "")

      exporters:
        otlphttp/newrelic:
          endpoint: "<YOUR_NEWRELIC_OTLP_ENDPOINT>"  # Replace with your region's endpoint
          headers:
            api-key: "<YOUR_NEW_RELIC_LICENSE_KEY>"   # Replace with your license key

      service:
        extensions: [health_check, k8s_observer]
        pipelines:
          metrics/nginx_prometheus:
            receivers: [receiver_creator/nginx_prom]
            processors: [batch, resource/cluster, metricstransform/nginx_prom, filter/nginx_prom_only, transform/nginx, transform/metadata_nullify]
            exporters: [otlphttp/newrelic]
    ```

    ###### Configuration parameters

    The following table describes the key configuration parameters:

    | Parameter                       | Description                                                                                                                                                                       |
    | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `<CLUSTER_NAME>`                | Your Kubernetes cluster name. Used for identification in New Relic.                                                                                                               |
    | `<YOUR_NEW_RELIC_LICENSE_KEY>`  | Your New Relic license key.                                                                                                                                                       |
    | `<YOUR_NEWRELIC_OTLP_ENDPOINT>` | Your region's OTLP endpoint. See [OTLP endpoint documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol). |
    | `app` and `role` labels         | Pod labels used to discover NGINX pods. Update the `rule` to match your pod labels.                                                                                               |
    | `name == "metrics"`             | The named container port on `9113` that the receiver_creator watches for. Must match the port name in your pod spec.                                                              |
    | `scrape_interval`               | How often to scrape Prometheus metrics. Default: `30s`.                                                                                                                           |

**Step 2: Install with Helm**

Follow the [OpenTelemetry Collector Helm chart installation guide](https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-collector/README.md#opentelemetry-collector-helm-chart) to install the collector using your custom `values.yaml` file.

```bash
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
helm upgrade --install nginx-otel-collector open-telemetry/opentelemetry-collector \
  --namespace newrelic --create-namespace \
  -f values.yaml
```

**Step 3: Verify deployment and data collection**

1.  Verify the collector pods are running:

    ```bash
    kubectl get pods -n newrelic --watch
    ```

2.  Check collector logs for successful pod discovery and scraping:

    ```bash
    kubectl logs -n newrelic -l app.kubernetes.io/name=opentelemetry-collector -f
    ```

    Look for lines referencing `receiver_creator/nginx_prom` discovering your NGINX pods.

3.  Run an NRQL query in New Relic to confirm data is arriving. Replace `<CLUSTER_NAME>` with your cluster name:

    ```sql
    FROM Metric SELECT *
    WHERE metricName LIKE 'nginx.%'
      AND instrumentation.provider = 'opentelemetry'
      AND k8s.cluster.name = '<CLUSTER_NAME>'
    SINCE 10 minutes ago
    ```

## View your data in New Relic [#find-data]

Once your setup is complete and data is flowing, you can access your NGINX metrics in New Relic dashboards and create custom alerts.

For complete instructions on accessing dashboards, querying data with NRQL, and creating alerts, see [Find and query your NGINX data](https://docs.newrelic.com/docs/opentelemetry/integrations/nginx/find-and-query-your-data).

## Metrics and attributes reference [#metrics]

This integration collects the same core NGINX metrics as the on-host deployment, with additional Kubernetes-specific resource attributes for cluster, namespace, and pod identification.

**For complete metrics and attributes reference:** See [NGINX OpenTelemetry metrics and attributes reference](https://docs.newrelic.com/docs/opentelemetry/integrations/nginx/nginx-otel-metrics-reference/) for detailed descriptions of all metrics, types, and resource attributes for Kubernetes deployments.

## Next steps [#next-steps]

**Explore related monitoring:**

-   [Monitor NGINX Plus with OpenTelemetry](https://docs.newrelic.com/docs/opentelemetry/integrations/nginx-plus/nginx-plus-otel/) - For commercial NGINX Plus deployments
-   [Monitor self-hosted NGINX with OpenTelemetry](https://docs.newrelic.com/docs/opentelemetry/integrations/nginx/nginx-otel-host/) - For traditional server deployments

**Kubernetes-specific resources:**

-   [OpenTelemetry Collector on Kubernetes](https://opentelemetry.io/docs/kubernetes/) - Advanced collector configurations
