This guide shows you how to instrument your Node.js or NestJS application with OpenTelemetry and send traces to SigNoz. You can use either zero-code automatic instrumentation or code-level setup depending on your needs.
Prerequisites
- Node.js 14 or later
- A SigNoz Cloud account or self-hosted SigNoz instance
Send traces to SigNoz
Step 1. Set environment variables
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
export OTEL_SERVICE_NAME="<service-name>"
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
Replace the following:
<region>: Your SigNoz Cloud region (us,eu, orin). See endpoints.<your-ingestion-key>: Your SigNoz ingestion key.<service-name>: Name of your service (e.g.,payment-service).
Step 2. Install OpenTelemetry packages
npm install --save @opentelemetry/api @opentelemetry/auto-instrumentations-node
Step 3. Run the application
node app.js
Need to enable or disable instrumentation for specific libraries? Check out our guide on selective instrumentation.
Step 1. Set environment variables
Add OpenTelemetry environment variables to your Kubernetes deployment manifest:
env:
- name: OTEL_TRACES_EXPORTER
value: 'otlp'
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: 'https://ingest.<region>.signoz.cloud:443'
- name: OTEL_NODE_RESOURCE_DETECTORS
value: 'env,host,os'
- name: OTEL_SERVICE_NAME
value: '<service-name>'
- name: OTEL_EXPORTER_OTLP_HEADERS
value: 'signoz-ingestion-key=<your-ingestion-key>'
- name: NODE_OPTIONS
value: '--require @opentelemetry/auto-instrumentations-node/register'
Replace the following:
<region>: Your SigNoz Cloud region (us,eu, orin). See endpoints.<your-ingestion-key>: Your SigNoz ingestion key.<service-name>: Name of your service (e.g.,payment-service).
Step 2. Install OpenTelemetry packages
Ensure your application image includes the OpenTelemetry packages:
npm install --save @opentelemetry/api @opentelemetry/auto-instrumentations-node
Step 3. Deploy and run
Apply your deployment to the cluster. The environment variables will configure OpenTelemetry automatically when your application starts.
Need to enable or disable instrumentation for specific libraries? Check out our guide on selective instrumentation.
The OpenTelemetry Operator auto-injects instrumentation into your Node.js pods without modifying your application image.
Step 1. Set up the OpenTelemetry Operator
Install the Operator and Collector following the K8s OTel Operator installation guide.
Step 2. Create the Instrumentation resource
Create instrumentation.yaml to configure Node.js auto-instrumentation:
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: nodejs-instrumentation
spec:
exporter:
endpoint: http://otel-collector-collector:4318
propagators:
- tracecontext
- baggage
nodejs:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest
Deploy this resource to your cluster.
Step 3. Add annotations to your deployment
Add these annotations to your pod template's metadata.annotations:
instrumentation.opentelemetry.io/inject-nodejs: "true"
Step 1. Set environment variables (PowerShell)
$env:OTEL_TRACES_EXPORTER = "otlp"
$env:OTEL_EXPORTER_OTLP_ENDPOINT = "https://ingest.<region>.signoz.cloud:443"
$env:OTEL_NODE_RESOURCE_DETECTORS = "env,host,os"
$env:OTEL_SERVICE_NAME = "<service-name>"
$env:OTEL_EXPORTER_OTLP_HEADERS = "signoz-ingestion-key=<your-ingestion-key>"
$env:NODE_OPTIONS = "--require @opentelemetry/auto-instrumentations-node/register"
Replace the following:
<region>: Your SigNoz Cloud region (us,eu, orin).<your-ingestion-key>: Your SigNoz ingestion key.<service-name>: Name of your service.
Step 2. Install OpenTelemetry packages
npm install --save @opentelemetry/api @opentelemetry/auto-instrumentations-node
Step 3. Run the application
node app.js
Need to enable or disable instrumentation for specific libraries? Check out our guide on selective instrumentation.
Step 1. Add OpenTelemetry to your Dockerfile
# ... your existing build steps ...
# Install OpenTelemetry packages
RUN npm install @opentelemetry/api@^1.9.0 @opentelemetry/auto-instrumentations-node@^0.67.0
# Set OpenTelemetry environment variables
ENV OTEL_TRACES_EXPORTER="otlp"
ENV OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
ENV OTEL_SERVICE_NAME="<service-name>"
ENV OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
ENV NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
# ... your CMD or ENTRYPOINT ...
Or pass them at runtime:
docker run \
-e OTEL_TRACES_EXPORTER="otlp" \
-e OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443" \
-e OTEL_NODE_RESOURCE_DETECTORS="env,host,os" \
-e OTEL_SERVICE_NAME="<service-name>" \
-e OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>" \
-e NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register" \
your-image:latest
Replace the following:
<region>: Your SigNoz Cloud region (us,eu, orin). See endpoints.<your-ingestion-key>: Your SigNoz ingestion key.<service-name>: Name of your service (e.g.,payment-service).
Step 2. Build and run
docker build -t my-nodejs-app . && docker run -d -p 8080:8080 my-nodejs-app
Need to enable or disable instrumentation for specific libraries? Check out our guide on selective instrumentation.
Validate
After running your instrumented application, verify traces appear in SigNoz:
- Generate traffic by making requests to your application.
- Open SigNoz and navigate to Services. Click Refresh and look for your service.
- Go to Traces and apply filters to see your application's traces.
Troubleshooting
Enable debug logging
Set the log level to see detailed configuration and span information:
export OTEL_LOG_LEVEL=debug
Look for span output like this in your console:
{
"traceId": "985b66d592a1299f7d12ebca56ca1fe3",
"parentId": "8d62a70aa335a227",
"name": "bar",
"id": "17ada85c3d55376a",
"kind": 0,
"timestamp": 1685674607399000,
"duration": 299,
"attributes": {},
"status": { "code": 0 },
"events": []
}
Short-lived applications (Lambda/Serverless)
If your application exits quickly, explicitly shutdown the SDK to flush all spans:
// Assumes 'sdk' is your NodeSDK instance from the setup
await sdk.shutdown();
Service not appearing in SigNoz
- Verify your
OTEL_EXPORTER_OTLP_ENDPOINTandOTEL_EXPORTER_OTLP_HEADERSare set correctly - Check network connectivity to the SigNoz endpoint
- Ensure your application is receiving traffic (OpenTelemetry buffers data before sending)
Code-Based Setup (Optional)
Step 1. Install OpenTelemetry packages
npm install --save @opentelemetry/api@^1.9.0 \
@opentelemetry/sdk-node@^0.208.0 \
@opentelemetry/auto-instrumentations-node@^0.67.0 \
@opentelemetry/exporter-trace-otlp-http@^0.208.0
Step 2. Create the tracing file
'use strict'
const process = require('process');
const opentelemetry = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const { ATTR_SERVICE_NAME } = require('@opentelemetry/semantic-conventions');
const exporterOptions = {
url: 'https://ingest.<region>.signoz.cloud:443/v1/traces',
headers: { 'signoz-ingestion-key': '<your-ingestion-key>' }
}
const traceExporter = new OTLPTraceExporter(exporterOptions);
const sdk = new opentelemetry.NodeSDK({
traceExporter,
instrumentations: [getNodeAutoInstrumentations()],
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: '<service-name>'
})
});
sdk.start();
process.on('SIGTERM', () => {
sdk.shutdown()
.then(() => console.log('Tracing terminated'))
.catch((error) => console.log('Error terminating tracing', error))
.finally(() => process.exit(0));
});
import process from 'process';
import * as opentelemetry from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { resourceFromAttributes } from '@opentelemetry/resources';
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
const exporterOptions = {
url: 'https://ingest.<region>.signoz.cloud:443/v1/traces',
headers: { 'signoz-ingestion-key': '<your-ingestion-key>' }
}
const traceExporter = new OTLPTraceExporter(exporterOptions);
const sdk = new opentelemetry.NodeSDK({
traceExporter,
instrumentations: [getNodeAutoInstrumentations()],
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: '<service-name>'
})
});
sdk.start();
process.on('SIGTERM', () => {
sdk.shutdown()
.then(() => console.log('Tracing terminated'))
.catch((error) => console.log('Error terminating tracing', error))
.finally(() => process.exit(0));
});
Replace the following:
<region>: Your SigNoz Cloud region (us,eu, orin). See endpoints.<your-ingestion-key>: Your SigNoz ingestion key.<service-name>: Name of your service (e.g.,payment-service).
Step 3. Load the tracing file before your app
node -r ./tracing.js app.js
Import the tracing file at the top of your entry point:
// index.ts or main.ts
import './tracing';
Then run:
npx ts-node src/index.ts
Need to enable or disable instrumentation for specific libraries? Check out our guide on selective instrumentation.
Setup OpenTelemetry Collector (Optional)
What is the OpenTelemetry Collector?
The OTel Collector acts as a middleman between your app and SigNoz. Instead of sending data directly, your application sends everything to the Collector first, which then forwards it along.
Why use it?
- Cleaning up data — Filter out noisy traces you don't care about, or remove sensitive info before it leaves your servers.
- Keeping your app lightweight — Let the Collector handle batching, retries, and compression instead of your application code.
- Adding context automatically — The Collector can tag your data with useful info like which Kubernetes pod or cloud region it came from.
- Future flexibility — Want to send data to multiple backends later? The Collector makes that easy without changing your app.
See Switch from direct export to Collector for step-by-step instructions.