This guide shows you how to send metrics from your Node.js application (Express, Fastify, Koa, NestJS, Runtime) to SigNoz using OpenTelemetry.
Prerequisites
- Node.js 18 or later (LTS versions 18, 20, or 22 are supported)
- A SigNoz Cloud account or self-hosted SigNoz instance
Send metrics to SigNoz
Step 1. Set environment variables
Set the following environment variables to configure the OpenTelemetry exporter:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export OTEL_SERVICE_NAME="<service-name>"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_TRACES_EXPORTER="none"
export OTEL_LOGS_EXPORTER="none"
export OTEL_METRIC_EXPORT_INTERVAL="60000"
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>: A descriptive name for your service (e.g.,payment-service).OTEL_METRIC_EXPORT_INTERVAL: How often to export metrics in milliseconds (default: 60000 for 60 seconds).
Step 1. Set environment variables
Add these environment variables to your deployment manifest:
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: 'https://ingest.<region>.signoz.cloud:443'
- name: OTEL_EXPORTER_OTLP_HEADERS
value: 'signoz-ingestion-key=<your-ingestion-key>'
- name: OTEL_SERVICE_NAME
value: '<service-name>'
- name: OTEL_METRICS_EXPORTER
value: 'otlp'
- name: OTEL_TRACES_EXPORTER
value: 'none'
- name: OTEL_LOGS_EXPORTER
value: 'none'
- name: OTEL_METRIC_EXPORT_INTERVAL
value: '60000'
- 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>: A descriptive name for your service (e.g.,payment-service).OTEL_METRIC_EXPORT_INTERVAL: How often to export metrics in milliseconds (default: 60000 for 60 seconds).
Step 1. Set environment variables (PowerShell)
$env:OTEL_EXPORTER_OTLP_ENDPOINT = "https://ingest.<region>.signoz.cloud:443"
$env:OTEL_EXPORTER_OTLP_HEADERS = "signoz-ingestion-key=<your-ingestion-key>"
$env:OTEL_SERVICE_NAME = "<service-name>"
$env:OTEL_METRICS_EXPORTER = "otlp"
$env:OTEL_TRACES_EXPORTER = "none"
$env:OTEL_LOGS_EXPORTER = "none"
$env:OTEL_METRIC_EXPORT_INTERVAL = "60000"
$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>: A descriptive name for your service.OTEL_METRIC_EXPORT_INTERVAL: How often to export metrics in milliseconds (default: 60000 for 60 seconds).
Step 1. Set environment variables in Dockerfile
Add environment variables to your Dockerfile:
# ... build stages ...
# Set OpenTelemetry environment variables
ENV OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
ENV OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
ENV OTEL_SERVICE_NAME="<service-name>"
ENV OTEL_METRICS_EXPORTER="otlp"
ENV OTEL_TRACES_EXPORTER="none"
ENV OTEL_LOGS_EXPORTER="none"
ENV OTEL_METRIC_EXPORT_INTERVAL="60000"
ENV NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
CMD ["node", "app.js"]
Or pass them at runtime using docker run:
docker run -e OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443" \
-e OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>" \
-e OTEL_SERVICE_NAME="<service-name>" \
-e OTEL_METRICS_EXPORTER="otlp" \
-e OTEL_TRACES_EXPORTER="none" \
-e OTEL_LOGS_EXPORTER="none" \
-e OTEL_METRIC_EXPORT_INTERVAL="60000" \
-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>: A descriptive name for your service (e.g.,payment-service).OTEL_METRIC_EXPORT_INTERVAL: How often to export metrics in milliseconds (default: 60000 for 60 seconds).
Step 2. Install OpenTelemetry packages
npm install @opentelemetry/api @opentelemetry/auto-instrumentations-node
Step 3. Add custom metrics
Add custom metrics to your application using metrics.getMeter():
const { metrics } = require('@opentelemetry/api');
// Get a meter - MeterProvider is auto-configured by auto-instrumentations-node
const meter = metrics.getMeter('my.meter.name');
// Create a counter instrument
const requestCounter = meter.createCounter('requests.total', {
unit: '1',
description: 'Total number of requests'
});
function handleRequest(endpoint) {
// Record the metric with attributes
requestCounter.add(1, { endpoint: endpoint });
// ... handle the request ...
}
module.exports = { handleRequest };
This example shows a Counter, which only increases. OpenTelemetry supports other metric types like UpDownCounter, Histogram, and Observable Gauge. See Custom Metrics Examples for complete examples of each type.
Step 4. Run your application
Run your application normally. The NODE_OPTIONS environment variable set in Step 1 automatically loads the auto-instrumentation:
node app.js
The auto-instrumentation package automatically generates HTTP metrics (http.server.request.duration and http.client.request.duration) for Node.js built-in http and https modules. Since frameworks like Express, Fastify, and Koa use these modules internally, HTTP metrics are captured automatically. See the HTTP instrumentation documentation for more details.
Validate
Once you have configured your application to start sending metrics to SigNoz, you can start visualizing the metrics in the metrics explorer.
Want more metrics? For system-level metrics like CPU, memory, and event loop stats, see Runtime Metrics.
Custom Metrics Examples
OpenTelemetry supports six metric instrument types. Here's a complete example demonstrating all of them:
Synchronous Instruments
Synchronous instruments are called inline with your application code.
Counter - A value that only increases (e.g., total requests, bytes sent):
const counter = meter.createCounter('requests.total', {
unit: '1',
description: 'Total number of requests'
});
// Record a value
counter.add(1, { endpoint: '/api/users' });
UpDownCounter - A value that can increase or decrease (e.g., active connections, queue size):
const updownCounter = meter.createUpDownCounter('connections.active', {
unit: '1',
description: 'Number of active connections'
});
// Increment
updownCounter.add(1);
// Decrement
updownCounter.add(-1);
Histogram - A distribution of values (e.g., request latency, response sizes):
const histogram = meter.createHistogram('request.duration', {
unit: 'ms',
description: 'Request duration in milliseconds'
});
// Record a value
histogram.record(45.5, { endpoint: '/api/users' });
Asynchronous Instruments
Asynchronous instruments use callback functions that are invoked during metric collection.
ObservableCounter - Observes a monotonically increasing value (e.g., CPU time, page faults):
const observableCounter = meter.createObservableCounter('system.cpu.time', {
unit: 's',
description: 'CPU time'
});
observableCounter.addCallback((observableResult) => {
// Return current cumulative value
observableResult.observe(getTotalCpuTime(), { cpu: 'cpu0' });
});
ObservableUpDownCounter - Observes a value that can go up or down (e.g., memory usage, thread count):
const observableUpDown = meter.createObservableUpDownCounter('process.memory.usage', {
unit: 'By',
description: 'Memory usage in bytes'
});
observableUpDown.addCallback((observableResult) => {
observableResult.observe(process.memoryUsage().heapUsed, {});
});
ObservableGauge - Observes a point-in-time value (e.g., temperature, current CPU utilization):
const gauge = meter.createObservableGauge('temperature.current', {
unit: 'Cel',
description: 'Current temperature'
});
gauge.addCallback((observableResult) => {
observableResult.observe(getCurrentTemperature(), { location: 'server-room' });
});
Runtime Metrics
To collect Node.js runtime metrics (event loop, V8 heap, garbage collection), use the @opentelemetry/instrumentation-runtime-node package.
Install
npm install @opentelemetry/instrumentation-runtime-node
Usage
Enable runtime metrics by setting the environment variable:
export OTEL_NODE_ENABLED_INSTRUMENTATIONS="runtime-node"
Then run your application as usual:
node --require @opentelemetry/auto-instrumentations-node/register app.js
By default, @opentelemetry/auto-instrumentations-node includes the runtime instrumentation. If you're using selective instrumentation and have disabled it, add runtime-node to your enabled instrumentations list.
Exported Metrics
The @opentelemetry/instrumentation-runtime-node package automatically exports the following Node.js runtime metrics:
Event Loop Metrics:
nodejs.eventloop.delay.min- Minimum event loop delaynodejs.eventloop.delay.max- Maximum event loop delaynodejs.eventloop.delay.mean- Mean event loop delaynodejs.eventloop.delay.stddev- Standard deviation of event loop delaynodejs.eventloop.delay.p50- 50th percentile of event loop delaynodejs.eventloop.delay.p90- 90th percentile of event loop delaynodejs.eventloop.delay.p99- 99th percentile of event loop delaynodejs.eventloop.time- Cumulative event loop time (active/idle)nodejs.eventloop.utilization- Event loop utilization ratio (0.0 to 1.0)
V8 Heap Memory Metrics:
v8js.memory.heap.limit- Total heap memory size pre-allocatedv8js.memory.heap.used- Heap memory size allocatedv8js.memory.heap.space.available_size- Heap space available sizev8js.memory.heap.space.physical_size- Committed size of a heap space
V8 Garbage Collection Metrics:
v8js.gc.duration- Garbage collection duration (histogram)
See the OpenTelemetry semantic conventions for the complete list:
Troubleshooting
Metrics not appearing?
Check Environment Variables: Ensure
OTEL_EXPORTER_OTLP_ENDPOINTis set correctly:- For gRPC (default):
https://ingest.<region>.signoz.cloud:443 - For HTTP:
https://ingest.<region>.signoz.cloud:443(SDK appends/v1/metricsautomatically)
- For gRPC (default):
Check Metrics Exporter: Ensure
OTEL_METRICS_EXPORTERis set tootlp(this is the default).Enable Debug Logging: Set the log level to see detailed output:
export OTEL_LOG_LEVEL=debugResource Attributes: Ensure
OTEL_SERVICE_NAMEis set. This helps you filter metrics by service in SigNoz.Verify Package Installation: Ensure all required packages are installed:
npm list @opentelemetry/api @opentelemetry/auto-instrumentations-node
Authentication errors
If you see errors like "Unauthorized" or "403 Forbidden":
- Verify your ingestion key is correct in
OTEL_EXPORTER_OTLP_HEADERS - Ensure the header format is exactly:
signoz-ingestion-key=<your-key>(no extra spaces) - Check that your ingestion key is active in the SigNoz Cloud dashboard
"Connection Refused" errors
- If running locally and sending to SigNoz Cloud, check your internet connection and firewall.
- If sending to a self-hosted collector, ensure the collector is running and listening on port 4317 (gRPC) or 4318 (HTTP).
SSL/TLS errors
If you encounter SSL certificate errors:
- Ensure you're using
https://in the endpoint URL - For self-hosted setups with self-signed certificates, you may need to configure the exporter to trust your CA
Setup OpenTelemetry Collector (Optional)
What is the OpenTelemetry Collector?
Think of the OTel Collector as a middleman between your app and SigNoz. Instead of your application sending data directly to SigNoz, it sends everything to the Collector first, which then forwards it along.
Why use it?
- Cleaning up data — Filter out noisy metrics 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 to convert your setup.
For more details, see Why use the OpenTelemetry Collector? and the Collector configuration guide.
Next Steps
- Create Dashboards to visualize your metrics.
- Set up Alerts on your metrics.
- Instrument your Node.js application with traces and logs for complete observability.