NGINX 1.25.3 and later includes the ngx_otel_module, a native OpenTelemetry module that adds distributed tracing support directly in the web server.
When you combine ngx_otel_module with instrumented applications, New Relic connects the traces end-to-end and creates service relationships between your services and the NGINX entity. These relationships appear in service maps, giving you visibility into how traffic flows through your web server or reverse proxy.
The client application sends a request with a W3Ctraceparent header.
NGINX's ngx_otel_module extracts the trace context, creates a span for the request, and injects updated trace context into the request proxied to the upstream backend.
The backend application receives the request with the propagated trace context and continues the trace.
All spans (from the client, NGINX, and the backend) are exported to an OpenTelemetry Collector, which enriches the NGINX spans with the NGINX identity and forwards them to New Relic.
New Relic uses these connected spans to create CALLS relationships:
Client service CALLS NGINX entity
NGINX entity CALLS Backend service
Because the collector stamps the same nginx.deployment.name and nginx.server.endpoint identity that the NGINX metrics use, the NGINX spans resolve to the sameNGINXSERVER entity as your NGINX metrics. These relationships are visible in service maps and the maps experience.
Compatibility
ngx_otel_module works with any application that supports W3C Trace Context propagation, including:
You can mix instrumentation approaches. For example, an OTel SDK client can call through NGINX to a New Relic APM agent backend, and the relationship chain appears correctly in New Relic.
OpenTelemetry Collector (NRDOT or OTel Collector Contrib) running on the same host or accessible from the NGINX host
Instrumented applications sending requests through NGINX, using any of the compatible instrumentation approaches listed above
Network access from the collector to the New Relic OTLP endpoint
ヒント
This guide sets up both metrics collection and distributed tracing in a single OTel Collector. For metrics-only configuration or Kubernetes deployment, see Monitor self-hosted NGINX and Monitor NGINX on Kubernetes.
Set up distributed tracing
ヒント
The traces pipeline below is the same standard setup you would use for any service participating in distributed tracing. It's not a manual relationship configuration. Once tracing is active, New Relic automatically detects the connected spans and creates service relationships.
Load the module and enable tracing in your nginx.conf. Add the load_module directive at the top level (main context), and the OpenTelemetry directives inside the http context:
# Main context — load the dynamic module
load_module modules/ngx_otel_module.so;
http{
# Export spans to the OpenTelemetry Collector over OTLP/gRPC
otel_exporter{
endpoint localhost:4317;
}
# A name that identifies this NGINX instance in traces
otel_service_name nginx-server;
# Emit a span per request and propagate W3C trace context to upstreams
otel_traceon;
otel_trace_context propagate;
server{
listen80;
location /{
proxy_pass http://backend_app;
}
}
}
This configuration:
Loads the OpenTelemetry module (load_module).
Exports spans over OTLP/gRPC to the collector listening on localhost:4317 (otel_exporter).
Creates a span for every request (otel_trace on). To trace a subset of traffic in high-throughput environments, set otel_trace to a variable (for example, driven by split_clients) instead of on.
Propagates trace context (otel_trace_context propagate): This both extracts the incoming traceparent header (linking NGINX spans to the calling service) and injects updated context into requests sent to upstreams (letting downstream services continue the trace).
The otel_trace on and otel_trace_context directives can also be set per server or location block if you want to trace only specific virtual hosts or routes.
Configure the OTel Collector to receive traces from ngx_otel_module and metrics from the NGINX stub status endpoint, enrich both with the NGINX identity, and forward them to New Relic.
receivers:
# Receives spans from ngx_otel_module (NGINX → localhost:4317)
otlp:
protocols:
grpc:
endpoint:"0.0.0.0:4317"
http:
endpoint:"0.0.0.0:4318"
# Collects NGINX metrics from the stub status endpoint
nginx:
endpoint: <YOUR_STUB_STATUS_ENDPOINT># e.g. http://127.0.0.1/status
collection_interval: 30s
processors:
resourcedetection:
detectors:[system]
system:
resource_attributes:
host.id:
enabled:true
# Adds the NGINX identity so spans and metrics resolve to the SAME NGINXSERVER entity
resource/nginx:
attributes:
-key: nginx.server.endpoint
value:"<YOUR_STUB_STATUS_ENDPOINT>"# must match the nginx receiver endpoint
action: upsert
-key: nginx.deployment.name
value:"<YOUR_DEPLOYMENT_NAME>"# a stable name for this NGINX deployment
action: upsert
# Sets nginx.display.name for the metrics pipeline
This collector configuration includes two pipelines:
Traces pipeline: Receives OTLP trace data from ngx_otel_module and your instrumented applications via gRPC (port 4317) or HTTP (port 4318). This is the same standard traces pipeline you would use for any service sending OTLP data to New Relic.
Metrics pipeline: Uses the nginxreceiver to collect performance metrics (connections, requests) from the NGINX stub status endpoint. These metrics create the NGINX entity in New Relic with golden metrics.
Both pipelines share these processors:
resourcedetection: Adds host.id, a standard resource attribute used to identify hosts across the OpenTelemetry ecosystem.
resource/nginx: Adds nginx.server.endpoint and nginx.deployment.name. These two attributes form the NGINX entity's identity. Applying them to both pipelines makes the NGINX spans and metrics resolve to the same NGINXSERVER entity instead of a duplicate service entity.
transform/nginx_*: Adds nginx.display.name for a friendly entity name.
重要
The nginx.server.endpoint value in resource/nginx and the nginx receiver endpoint must be identical. Together with nginx.deployment.name, they form the NGINXSERVER entity's identity. This is a one-time, static value per NGINX instance. When you add or remove backend and client applications, you don't need to change the collector configuration. Relationships form automatically through trace context propagation.
Set the required environment variables and start (or restart) the collector:
Test the configuration and reload NGINX to load the OpenTelemetry module:
bash
$
sudo nginx -t
$
sudo systemctl reload nginx
重要
The load_module directive requires that the ngx_otel_module.so file exists at the given path and matches your NGINX version. If you see unknown directive "otel_exporter" or a module load error, the module is not installed or not loaded. Install the nginx-module-otel package for your NGINX version and confirm the load_module path.
Once NGINX and the collector are running, generate some traffic through your instrumented applications. After a few minutes, verify data is arriving in New Relic:
-- Verify NGINX trace spans
FROM Span SELECTcount(*)
WHERE nginx.deployment.name ='<YOUR_DEPLOYMENT_NAME>'
SINCE 10 minutes ago
-- Verify NGINX metrics
FROM Metric SELECTcount(*)
WHERE metricName LIKE'nginx.%'
SINCE 10 minutes ago
After trace data is flowing, New Relic automatically creates CALLS relationships between your services and the NGINX entity. It may take up to 10 minutes for relationships to appear.
WHERE source.entityName ='<YOUR_NGINX_DISPLAY_NAME>'
OR target.entityName ='<YOUR_NGINX_DISPLAY_NAME>'
SINCE 1day ago
Troubleshooting
Verify NGINX reloaded without errors: sudo nginx -t and sudo journalctl -u nginx -n 50 --no-pager
Confirm ngx_otel_module is loaded. An unknown directive "otel_exporter" error means the module isn't loaded. Check the load_module path and that nginx-module-otel is installed for your NGINX version.
Verify the OTel Collector is running and listening on the port in otel_exporter: sudo ss -tlnp | grep 4317
Confirm the otel_exporter endpoint matches the collector's gRPC listener address.
Allow up to 10 minutes for relationships to appear after the first spans arrive.
Verify that your instrumented applications are sending traces through the collector. Both the NGINX spans and the application spans must reach New Relic for relationships to form.
Check that your client applications propagate W3C traceparent headers. Without trace context propagation, NGINX spans aren't connected to the calling service.
Confirm both the resourcedetection and resource/nginx processors are included in the collector traces pipeline. The nginx.server.endpoint and nginx.deployment.name attributes are required for the NGINX spans to resolve to the NGINXSERVER entity.
Confirm otel_trace_context propagate is set (not extract or inject alone). propagate is required for end-to-end context flow through NGINX.
Query to verify both NGINX and application spans share trace IDs:
FROM Span SELECT uniques(service.name)
WHERE trace.id IN(
SELECT uniques(trace.id)FROM Span
WHERE nginx.deployment.name ='<YOUR_DEPLOYMENT_NAME>'
SINCE 10 minutes ago LIMIT5
)
SINCE 10 minutes ago
You should see your NGINX service alongside your application service names.
Ensure the resource/nginx processor runs in the traces pipeline (not only the metrics pipeline). Without it, NGINX spans lack nginx.deployment.name / nginx.server.endpoint and are synthesized as a generic service rather than resolving to the NGINXSERVER entity.
Verify the nginx.server.endpoint value is identical in the nginx receiver and the resource/nginx processor, and that nginx.deployment.name matches the value used by your NGINX metrics. The entity identity is the composite of these two values. A mismatch produces a different entity.
Run the metrics pipeline too. The NGINXSERVER entity's golden metrics come from the nginxreceiver; without it, NGINX still appears via traces but without metrics.
Each instrumented application must send traces through the same OTel Collector (or directly to New Relic) so that span data for all services reaches the same account.
For applications using New Relic APM agents, verify that distributed tracing is enabled and the agent is connected.
For OTel SDK applications, verify the OTLP exporter is configured to send to the collector.
Allow additional time. Relationships for services with lower traffic volume may take longer to appear.
Next steps
Service maps: Learn how to explore entity relationships visually