Instrumenting a Node.js service with the Elastic Distribution of OpenTelemetry for Node.js adds Elastic-supported OpenTelemetry telemetry without loading the legacy Elastic APM agent in the same process. It fits Express, HTTP, and other supported Node.js services that need automatic spans and metrics in Elastic Observability.

The EDOT package, @elastic/opentelemetry-node, starts during the Node.js preload phase through the --import option. Starting it before application modules load allows the SDK to patch supported libraries such as http and express before the service handles traffic.

EDOT Node.js reads standard OTEL_* environment variables and sends telemetry by OTLP to either an Elastic Cloud Managed OTLP endpoint or an EDOT Collector that exports to Elastic. Keep the Elastic write key in the deployment secret, service manager, or Collector configuration, and use one instrumentation agent per process to avoid duplicate spans.

Steps to instrument a Node.js service with EDOT Node.js:

  1. Check the Node.js runtime that starts the service.
    $ node --version
    v22.22.3

    EDOT Node.js supports Node.js 18.19.0, 20.6.0, or later. Check the runtime used by the service manager or container image, not only the local developer shell.

  2. Install the EDOT Node.js package in the service project.
    $ npm install @elastic/opentelemetry-node
    added 273 packages in 12s
  3. Remove any existing Elastic APM agent preload from the same process.
    Old start option:
    --require elastic-apm-node/start
    
    New start option:
    --import @elastic/opentelemetry-node

    Do not run EDOT Node.js beside another APM agent in the same Node.js process. Multiple agents can patch the same modules and produce duplicate or conflicting telemetry.

  4. Set the EDOT environment for the service.
    edot-node.env
    export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp.elastic.example.net"
    export OTEL_EXPORTER_OTLP_HEADERS="Authorization=ApiKey REDACTED_API_KEY"
    export OTEL_SERVICE_NAME="checkout-api"

    Use the Managed OTLP endpoint for Elastic Cloud, or use the local EDOT Collector endpoint when a Collector owns the Elastic credentials. Self-managed, ECE, and ECK deployments need an OTLP-compatible Collector or gateway path instead of the Elastic Cloud Managed OTLP endpoint.

  5. Start the service with the EDOT preload before the application file.
    $ node --import @elastic/opentelemetry-node server.cjs
    {"name":"elastic-otel-node","msg":"start EDOT Node.js"}
    checkout-api listening on http://127.0.0.1:3000

    Use NODE_OPTIONS=“--import @elastic/opentelemetry-node” when a process manager owns the final node command and only accepts environment variables.

  6. Send a request that exercises the service path.
    $ curl --silent http://127.0.0.1:3000/checkout
    {"status":"paid","id":"order-1001"}

    The first request gives EDOT a trace to export. Use a route that passes through the framework, middleware, and downstream calls that should appear in Elastic Observability.

  7. Confirm the service in Elastic Observability.
    Kibana -> Observability -> Applications -> Service Inventory
    Service: checkout-api
    Transaction: GET /checkout

    The service can take a minute or two to appear after startup and traffic. If it does not appear, check the service name, OTLP endpoint, Authorization header, and Collector or endpoint logs.