Testing an OTLP/gRPC endpoint with grpcurl confirms that a Collector or backend listener accepts the OpenTelemetry trace export service before application instrumentation becomes the next suspect. It fits cases where a client cannot send telemetry and the first question is whether the endpoint, transport mode, and protobuf service are reachable.

grpcurl needs the OTLP protobuf schema because many OTLP receivers do not expose gRPC server reflection. The smoke test loads the official trace service definition and calls the trace export RPC directly instead of relying on list or describe.

Use -plaintext only for a local or explicitly plaintext receiver. For a TLS endpoint, omit -plaintext and add --cacert when the server certificate chains to a private CA. A successful export response proves the receiver accepted the request, but backend storage still needs a pipeline or backend-side check.

Steps to test an OTLP gRPC endpoint with grpcurl:

  1. Fetch the official OpenTelemetry protobuf definitions.
    $ git clone --depth 1 https://github.com/open-telemetry/opentelemetry-proto.git

    Skip this step when the opentelemetry-proto source or an equivalent protoset is already available.

  2. Create a small OTLP trace export request.
    otlp-trace.json
    {
      "resourceSpans": [
        {
          "resource": {
            "attributes": [
              {
                "key": "service.name",
                "value": {
                  "stringValue": "checkout-api"
                }
              }
            ]
          },
          "scopeSpans": [
            {
              "scope": {
                "name": "manual-grpcurl"
              },
              "spans": [
                {
                  "traceId": "W47/95gDgQPSabYzgT/GDA==",
                  "spanId": "7uGbfsPBsXQ=",
                  "name": "grpcurl-otlp-smoke",
                  "kind": 1,
                  "startTimeUnixNano": "1717260000000000000",
                  "endTimeUnixNano": "1717260001000000000"
                }
              ]
            }
          ]
        }
      ]
    }

    grpcurl uses protobuf JSON encoding. Byte fields such as traceId and spanId must be base64 strings, while the debug exporter later prints those same IDs as hexadecimal.

  3. Send the trace request to the OTLP/gRPC endpoint.
    $ grpcurl -plaintext \
      -import-path opentelemetry-proto \
      -proto opentelemetry/proto/collector/trace/v1/trace_service.proto \
      -d @ collector.example.net:4317 \
      opentelemetry.proto.collector.trace.v1.TraceService/Export < otlp-trace.json
    {
      "partialSuccess": {}
    }

    Replace collector.example.net:4317 with the endpoint under test. For a TLS endpoint, remove -plaintext; for a private CA, add --cacert before the endpoint.

  4. Treat a reflection failure as separate from the export test.
    $ grpcurl -plaintext collector.example.net:4317 list
    Failed to list services: server does not support the reflection API

    OTLP receivers commonly accept TraceService/Export without enabling server reflection. Use the explicit -proto export call as the endpoint check.

  5. Check the Collector or backend evidence when the endpoint is under your control.
    $ journalctl -u otelcol --since "5 minutes ago" --no-pager
    ##### snipped #####
    Traces {"otelcol.component.id":"debug","otelcol.component.kind":"exporter","otelcol.signal":"traces","resource spans":1,"spans":1}
    Resource attributes:
         -> service.name: Str(checkout-api)
    InstrumentationScope manual-grpcurl
    Span #0
        Trace ID       : 5b8efff798038103d269b633813fc60c
        ID             : eee19b7ec3c1b174
        Name           : grpcurl-otlp-smoke

    The grpcurl response confirms the receiver accepted the request. The log or backend view confirms the trace reached the expected pipeline or storage path.
    Related: How to test OpenTelemetry Collector pipelines with the debug exporter

  6. Remove the temporary protobuf checkout and request file when the check is finished.
    $ rm -rf opentelemetry-proto otlp-trace.json