---
title: "Application Observability host hours pricing | Grafana Cloud documentation"
description: "How Application Observability identifies hosts for host hours billing, and how to configure your telemetry so hosts are counted correctly."
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# Application Observability host hours pricing

Application Observability bills on **host hours**, the hours each host spends sending application telemetry to Grafana Cloud, plus the telemetry your instrumented applications send, at standard Grafana Cloud rates.

For the pricing model, including what counts as a host, how usage is calculated, which pricing model applies to you, and how serverless environments are billed, refer to [Application Observability pricing](/docs/grafana-cloud/platform/pricing-and-usage/application-observability/) and [Host hours billing](/docs/grafana-cloud/platform/pricing-and-usage/host-hours/). For current rates, refer to the [Grafana Cloud pricing page](/pricing/#application-observability).

This page explains how Application Observability identifies hosts from your telemetry, and how to configure your collectors so every host is counted correctly.

## How Application Observability identifies hosts

Application Observability identifies hosts from standard OpenTelemetry resource attributes attached to your telemetry. To make billing work, two things matter: which [metric-generation path](#which-metric-generation-path-applies-to-you) your telemetry follows on its way to Grafana Cloud, and which host resource attribute is present on the resulting metrics. The priority list below shows the supported attributes; the rest of this section walks through both.

For every service, Application Observability evaluates the following resource attributes in priority order and uses the **first match** as the host’s billing identifier — you only need to satisfy one tier, not all three:

Expand table

| Priority | Attributes        | Typical environment                                                         |
|----------|-------------------|-----------------------------------------------------------------------------|
| 1        | `k8s.node.name`   | Kubernetes workloads                                                        |
| 2        | `host.id`         | Cloud VMs, bare metal, and on-premises VMs (non-Kubernetes)                 |
| 3        | `grafana.host.id` | Docker, and any host where `host.id` isn’t available (last-resort fallback) |

Priority 3 (`grafana.host.id`) is **not** emitted automatically by any tool — it’s an explicit opt-in attribute that you set deliberately when neither priority 1 nor priority 2 is available in your environment.

If none of these attributes are present on a service’s telemetry, the service is not counted for host hours billing and Application Observability surfaces a configuration warning. This is the safe failure mode — services without a verifiable host identifier are excluded rather than billed under an unreliable key.

### Which metric-generation path applies to you?

Application Observability supports three paths for the metrics that feed host identification. For all three, the host resource attribute must be present on the resulting metrics — see [Make sure your hosts are identified](#make-sure-your-hosts-are-identified) for per-environment setup. Each path differs in how the metrics are produced and whether any extra pipeline tagging is needed:

- **Tempo metrics-generator (default)**: Traces sent to Grafana Cloud are processed by Tempo’s metrics-generator, which derives RED metrics from them.
- **Beyla/OBI eBPF auto-instrumentation**: Beyla (or its upstream OpenTelemetry counterpart, OBI) emits RED metrics directly.
- **Collector-side `spanmetrics` connector**: You generate RED metrics in your own Grafana Alloy or OpenTelemetry Collector via the `spanmetrics` connector, and spans don’t reach Tempo’s metrics-generator. Tag the connector’s output metrics with `source=spanmetrics` (see [Set `source=spanmetrics`](#set-source-spanmetrics)) — the connector doesn’t add the attribute on its own.

If you’re not sure which path applies, the default is the Tempo metrics-generator path for traces from the OpenTelemetry SDKs / Alloy / OpenTelemetry Collector. You’re on the collector-side path only if you’ve explicitly configured a `spanmetrics` connector in your collector. To check, search your collector configuration file for `connectors:` sections containing `spanmetrics` (OpenTelemetry Collector) or `otelcol.connector.spanmetrics` (Grafana Alloy).

### Make sure your hosts are identified

Confirm one of the priority tiers is satisfied for each environment that sends telemetry to Application Observability. Start with priority 1 if you run on Kubernetes, priority 2 if you run on cloud VMs, and only fall back to priority 3 when neither applies.

#### Priority 1: `k8s.node.name` (Kubernetes)

Run the [`k8sattributes` processor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/k8sattributesprocessor) on your collector or gateway so it stamps `k8s.node.name` on incoming telemetry. The Grafana Cloud Kubernetes Monitoring Helm chart configures this for you. Beyla emits `k8s.node.name` natively on Kubernetes — no additional configuration is needed.

If you instrument applications with the OpenTelemetry SDK directly (no collector in the path), inject `k8s.node.name` via the [Downward API](https://kubernetes.io/docs/concepts/workloads/pods/downward-api/):

YAML ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
spec:
  containers:
    - name: app
      env:
        - name: K8S_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: OTEL_RESOURCE_ATTRIBUTES
          value: "k8s.node.name=$(K8S_NODE_NAME)"
```

> Note
> 
> On Kubernetes (identified by `k8s.cluster.name`), the node is the billing identifier and `host.id` (priority 2) is ignored — so a Kubernetes workload with no `k8s.node.name` isn’t counted.

#### Priority 2: `host.id` (cloud VMs, bare metal, on-premises)

`host.id` identifies any non-Kubernetes host — a cloud VM, bare-metal server, or on-premises VM. It must be an *opaque machine identifier*: a cloud instance ID (AWS `i-…`, GCP numeric ID, Azure `vmId`) or an OS machine ID (`/etc/machine-id` on Linux, the machine GUID on Windows). Standard OpenTelemetry resource detectors and Beyla produce these automatically — you just enable them.

##### Cloud VMs (AWS, GCP, Azure)

Enable the cloud-specific [resource detector](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/resourcedetectionprocessor) on your collector agent. It queries the cloud Instance Metadata Service (IMDS) and sets `host.id` to the instance ID (`cloud.platform` is also useful — it lets Application Observability apply serverless billing correctly):

Grafana AlloyOpenTelemetry Collector

Grafana Alloy OpenTelemetry Collector

Alloy ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```alloy
// Substitute ec2 with gcp or azure as appropriate
otelcol.processor.resourcedetection "default" {
  detectors = ["env", "ec2"]

  ec2 {
    resource_attributes {
      host.id        { enabled = true }
      host.name      { enabled = true }
      cloud.provider { enabled = true }
      cloud.platform { enabled = true }
    }
  }

  // output: wire to your next component
  output {}
}
```

YAML ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
# AWS EC2 — substitute gcp or azure as appropriate
processors:
  resourcedetection:
    detectors: [env, ec2]
    override: true
    ec2:
      resource_attributes:
        host.id: {enabled: true}
        host.name: {enabled: true}
        cloud.provider: {enabled: true}
        cloud.platform: {enabled: true}
# Add resourcedetection to service.pipelines processors
```

Beyla detects `host.id` natively on cloud VMs (via IMDS) — no additional configuration is needed.

##### Bare metal and on-premises VMs

Enable the `system` detector’s `host.id`, which reads the OS machine ID (`/etc/machine-id` on Linux, the machine GUID on Windows). It is disabled by default, so you must enable it explicitly:

Grafana AlloyOpenTelemetry Collector

Grafana Alloy OpenTelemetry Collector

Alloy ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```alloy
otelcol.processor.resourcedetection "default" {
  detectors = ["env", "system"]

  system {
    hostname_sources = ["dns", "os"]

    resource_attributes {
      host.id   { enabled = true }
      host.name { enabled = true }
    }
  }

  // output: wire to your next component
  output {}
}
```

YAML ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
processors:
  resourcedetection:
    detectors: [env, system]
    system:
      hostname_sources: [dns, os]
      resource_attributes:
        host.id: {enabled: true}
        host.name: {enabled: true}
# Add resourcedetection to service.pipelines processors
```

Beyla reads the machine ID natively on bare metal — no additional configuration is needed.

> Note
> 
> Don’t enable the `system` detector’s `host.id` inside Docker containers — the machine ID comes from the container image, not the host, so many containers collapse to a single `host.id`. Use `grafana.host.id` for Docker (priority 3).

#### Priority 3: `grafana.host.id` (Docker and last-resort fallback)

`grafana.host.id` is the explicit opt-in identifier. Use it for Docker — where containers have no per-host machine ID — and for any environment where neither `k8s.node.name` nor an opaque `host.id` is available. No tool emits `grafana.host.id` automatically; you set it deliberately on your collector.

##### Docker

Set `OTEL_RESOURCE_ATTRIBUTES` to inject the host identifier. Replace `my-server-hostname` with your host’s actual identifier:

DockerDocker Compose

Docker Docker Compose

Bash ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```bash
# Static value
docker run -e OTEL_RESOURCE_ATTRIBUTES="grafana.host.id=prod-web-01" ...

# Or use dynamic hostname
docker run -e OTEL_RESOURCE_ATTRIBUTES="grafana.host.id=$(hostname)" ...
```

YAML ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
services:
  collector:
    environment:
      # Replace with your host's actual identifier
      OTEL_RESOURCE_ATTRIBUTES: "grafana.host.id=prod-web-01"
```

Then configure your collector to read it:

Grafana AlloyOpenTelemetry Collector

Grafana Alloy OpenTelemetry Collector

Alloy ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```alloy
otelcol.processor.resourcedetection "default" {
  detectors = ["env"]

  // output: wire to your next component
  output {}
}
```

YAML ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
processors:
  resourcedetection:
    detectors: [env]
# Add resourcedetection to service.pipelines processors
```

### Passthrough gateway

If you run an OpenTelemetry Collector as a passthrough gateway in front of a collector agent that has already set host attributes, set `override: false` on the gateway’s `resourcedetection` processor. The default of `true` causes the gateway to silently overwrite the agent’s host attributes with its own identity, which leads to over-counting:

Grafana AlloyOpenTelemetry Collector

Grafana Alloy OpenTelemetry Collector

Alloy ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```alloy
otelcol.processor.resourcedetection "default" {
  detectors = ["env"]
  override  = false

  // output: wire to your next component
  output {}
}
```

YAML ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
processors:
  resourcedetection:
    detectors: [env]
    override: false
# Add resourcedetection to service.pipelines processors
```

### Set `source=spanmetrics`

On the [collector-side `spanmetrics` path](#which-metric-generation-path-applies-to-you), tag the metrics the connector produces with a `source=spanmetrics` resource attribute so Application Observability recognizes their `target_info` series for host identification. Add a `transform` processor **after** the `spanmetrics` connector and before the exporter that sets the attribute. Using `context: scope` runs the statement once per scope rather than once per data point; the `where name == "spanmetricsconnector"` condition scopes the change to metrics the connector produced, leaving any other metrics that pass through the processor untouched:

Grafana AlloyOpenTelemetry Collector

Grafana Alloy OpenTelemetry Collector

Alloy ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```alloy
otelcol.processor.transform "add_source" {
  error_mode = "ignore"
  metric_statements {
    context = "scope"
    statements = [
      `set(resource.attributes["source"], "spanmetrics") where name == "spanmetricsconnector"`,
    ]
  }

  output {
    metrics = [otelcol.processor.batch.default.input]
  }
}
```

Wire the connector’s metrics output into the processor so every generated metric is tagged before export:

Alloy ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```alloy
otelcol.connector.spanmetrics "default" {
  // ... your span metrics configuration ...

  output {
    metrics = [otelcol.processor.transform.add_source.input]
  }
}
```

YAML ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
processors:
  transform/add_source:
    metric_statements:
      - context: scope
        statements:
          - set(resource.attributes["source"], "spanmetrics") where name == "spanmetricsconnector"

service:
  pipelines:
    # the metrics pipeline fed by the spanmetrics connector
    metrics/spanmetrics:
      receivers: [spanmetrics]
      processors: [transform/add_source, batch]
      exporters: [prometheusremotewrite]
```

### Configure the host\_info / grafanacloud connector

> Warning
> 
> The `host_info` / `grafanacloud` connector and the `traces_host_info` metric it emits are deprecated but supported for the time being. New setups should send the host resource attributes listed in [How Application Observability identifies hosts](#how-application-observability-identifies-hosts) instead.

The connector inspects each span’s resource attributes in priority order and emits a `traces_host_info` metric tagged with the resolved host identifier. Configure `host_identifiers` to mirror the host-identification priority above — `k8s.node.name`, `host.id`, then `grafana.host.id`.

Grafana AlloyOpenTelemetry Collector

Grafana Alloy OpenTelemetry Collector

Alloy ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```alloy
otelcol.connector.host_info "default" {
  host_identifiers = ["k8s.node.name", "host.id", "grafana.host.id"]

  output {
    metrics = [otelcol.processor.batch.default.input]
  }
}
```

Wire the connector into the traces output of your OTLP receiver so every received span flows to it:

Alloy ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```alloy
otelcol.receiver.otlp "default" {
  grpc { }
  http { }

  output {
    traces = [
      otelcol.processor.batch.default.input,
      otelcol.connector.host_info.default.input,
    ]
  }
}
```

For the full Alloy configuration, refer to the [Alloy setup documentation](/docs/grafana-cloud/monitor-applications/application-observability/setup/collector/grafana-alloy/).

YAML ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```yaml
connectors:
  grafanacloud:
    host_identifiers: [k8s.node.name, host.id, grafana.host.id]

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/tempo, grafanacloud]
    metrics/host_info:
      receivers: [grafanacloud]
      processors: [batch]
      exporters: [prometheusremotewrite]
```

For the full OpenTelemetry Collector configuration, refer to the [OpenTelemetry Collector setup documentation](/docs/opentelemetry/collector/opentelemetry-collector/).

## The `traces_host_info` metric

If your Grafana Cloud stack receives the `traces_host_info` metric — produced when a `host_info` (Grafana Alloy) or `grafanacloud` (OpenTelemetry Collector) connector is wired into a collector pipeline that sends to the stack — Application Observability uses it as the host identifier for the whole stack rather than the resource attributes listed in [How Application Observability identifies hosts](#how-application-observability-identifies-hosts), even when those attributes are present. The switch is stack-wide: to have hosts identified from resource attributes instead, you must remove the connector from every collector pipeline writing to the stack, so that `traces_host_info` is no longer ingested. Migrating one collector or one service at a time is not possible.

## Host identification notification

If Application Observability can’t identify a host for one or more of your services, the system displays a notification. To resolve it, follow the [host identification checklist](#make-sure-your-hosts-are-identified) for your environment.

If you don’t plan to use Application Observability any longer, [read how to disable it to avoid charges](#disable-application-observability-in-grafana-cloud).

## Disable Application Observability in Grafana Cloud

If you don’t want to use Application Observability in Grafana Cloud or incur billing, you can disable it.

> Caution
> 
> If you follow these steps, you lose the visibility provided by Grafana Cloud Application Observability.

1. Select **Configuration** under **Application** in the left navigation of your Grafana Cloud instance.
2. Click the **System** tab.
3. Under **Deactivate Application Observability**, click **Deactivate**.
