How to Enable n8n OpenTelemetry Tracing: Workflow Spans, Node Spans, and Slow-Node Debugging
Introduction
A production n8n instance fails in ways the execution list cannot explain. A workflow shows as successful, yet a customer waited twenty minutes. A run shows as failed, yet every node looks fine when you open it afterwards. The execution view answers what happened to one run, not which step is slow across a thousand runs, which node fails most often, or where a request spent its time before reaching n8n. Native OpenTelemetry tracing closes that gap without a sidecar, a custom image, or a third-party plugin. This tutorial takes you from a blank setup to your first trace and the tuning that keeps it affordable.
Quick answer
Enable native OpenTelemetry tracing in n8n 2.19 or later by setting N8N_OTEL_ENABLED=true and N8N_OTEL_EXPORTER_OTLP_ENDPOINT to your collector on port 4318, or use Settings > OpenTelemetry on 2.27 or later, then run a workflow and inspect the trace.
Evidence
The version history matters more than usual here, because the setup path depends on the version you run. Tracing arrived in n8n 2.19.0 as a preview feature, with a warning that it may change and should not be relied on blindly in production. The graphical path, Settings > OpenTelemetry, arrived in 2.27.0 and applies changes without a restart. The gRPC protocol arrived in 2.39.0, crashed-execution spans in 2.42.0, and start spans in 2.44.0. Those five version numbers, all from n8n’s own documentation as of October 2026, define what your instance can do.
As n8n put it in its announcement: “n8n now supports OpenTelemetry natively, allowing every workflow execution to be emitted as a trace, so n8n shows up inside the same observability stack your team already uses for the rest of production.” Each execution produces a root span carrying status, duration, and error information, and an optional child span per node shows which step is slow or failing. Because n8n emits spans over OTLP HTTP using Protobuf encoding, the same setup feeds Jaeger, Tempo, Datadog, Honeycomb, New Relic, or Grafana Cloud. The official n8n OpenTelemetry documentation is the reference for every variable below, and the OpenTelemetry OTLP exporter configuration documents the standard ports: 4318 for HTTP, 4317 for gRPC.
Prerequisites
- A self-hosted n8n instance running version 2.19.0 or later (2.27.0 or later if you want the graphical setup path).
- Docker and Docker Compose on the same host, for the local Jaeger backend.
- Instance owner or admin access, which n8n requires for configuring OpenTelemetry in the user interface.
- A workflow you can run repeatedly without side effects.
Step 1: Confirm your version and pick a setup path
Check your version before you start, because it decides whether the graphical path exists at all. Run n8n --version on the host, check Settings in the editor, or for Docker run docker images | grep n8n to see the image tag. On 2.27.0 or later, you can use Settings > OpenTelemetry for day-to-day changes; on 2.19.0 through 2.26.x, environment variables are your only path.
One rule for later: an option set by environment variable wins over the interface, and n8n disables the matching field while the variable is set. To manage a setting from the interface, leave its variable unset. If you need to upgrade to reach a newer tracing feature, follow our guide to updating self-hosted n8n safely first, and pin your Docker image tag to a specific version rather than floating on latest.
Step 2: Start a local Jaeger backend
Jaeger is the fastest backend for a first trace, because it accepts OTLP directly and needs no collector in between. Save this as docker-compose.yml in a scratch directory; it is taken from n8n’s documentation for trying tracing with Jaeger:
services:
jaeger:
image: jaegertracing/jaeger:latest
ports:
- "16686:16686" # UI
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
Start it with:
docker compose up -d
Open http://localhost:16686: you should see the Jaeger search page with an empty service list, as expected, since n8n sends no traces yet. If port 4318 is already in use, stop the conflicting process or change the host-side mapping, and point n8n at the port you expose.
Step 3: Enable tracing with environment variables
This path works on every supported version, and it is the one to use in production, because it is version-controlled with your deployment. Add the following environment variables to your n8n service in your production docker-compose.yml, then recreate the container:
services:
n8n:
environment:
- N8N_OTEL_ENABLED=true
- N8N_OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318
Two details deserve attention. First, the endpoint is the base URL of your collector, not the traces path. n8n appends /v1/traces by default, so pointing the variable at http://jaeger:4318/v1/traces produces a doubled path and no traces. Second, the hostname is the Compose service name, jaeger, since both share a network; on a different host, use its real hostname or IP.
Restart n8n after changing environment variables, since they only take effect on restart. In queue mode, set the same variables on the main instance, every worker, and every webhook processor, or workers will silently produce no spans.
Step 4: Or enable tracing from Settings > OpenTelemetry
If you run n8n 2.27.0 or later and you are an instance owner or admin, the graphical path avoids a restart entirely. Open Settings > OpenTelemetry, turn on Enable OpenTelemetry, enter the OTLP endpoint under Collector connection, set sampling and span options under Tracing, and save. n8n applies the changes without a restart and reloads them across workers and webhook processors in queue mode.
Before you save, use the built-in check: under Verify configuration, select Send test trace. n8n sends a single test span and reports whether the collector accepted it. A failed test trace means connectivity, authentication, or endpoint trouble, not a workflow problem; a passing test trace with no real executions points to sampling or the production-only default, covered in troubleshooting below.
Step 5: Run a workflow and read its first trace
Build a test workflow with three nodes: a Webhook trigger, an HTTP Request node calling https://httpbin.org/get, and a Set node keeping one response field. Activate it, then send a request:
curl -X POST https://your-n8n-host/webhook/test-trace
Replace your-n8n-host with your instance hostname and test-trace with your webhook path. Open the Jaeger UI at http://localhost:16686, select n8n as the service, and click Find Traces. A trace holds two kinds of spans. The workflow.execute span is the root, recording workflow ID, name, version, project, node count, execution mode, status, and error type. Each nested node.execute span records node ID, name, type, version, and input and output item counts. Every span also carries instance attributes: service.name (default n8n), service.version, n8n.instance.id, and n8n.instance.role (main, worker, or webhook).
Read the trace the way an on-call engineer would: sort child spans by duration, and check the longest span’s status before blaming it, since a long span that succeeded is a slow dependency, while a short span with an error is a fast failure. A missing node span means that node never ran, which usually points to a branch condition or an early error, not a tracing problem.
Step 6: Trace a request end to end with traceparent
The real payoff is continuity across services. If a webhook request includes a W3C traceparent header, n8n uses it as the parent for the workflow span. Prove it with an explicit traceparent:
curl -X POST https://your-n8n-host/webhook/test-trace \
-H "traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
Outbound, HTTP Request nodes can inject traceparent into calls, so downstream services supporting W3C trace context continue the same trace ID. Sub-workflow spans parent to the calling span, and a resumed workflow’s new span links back to the previous one, so long runs stay traceable across pauses. To stop outbound injection, set N8N_OTEL_TRACES_INJECT_OUTBOUND=false.
Step 7: Tune tracing for production
On a busy instance, untuned tracing can flood a backend, so start with sampling. By default, n8n exports every trace; to export 10 percent, set:
N8N_OTEL_TRACES_SAMPLE_RATE=0.1
n8n uses a trace ID ratio sampler, so sampled traces stay complete. Next, control span volume: each node produces its own span, so a 40-node workflow produces 41 spans per execution. For workflow-level spans only, set:
N8N_OTEL_TRACES_INCLUDE_NODE_SPANS=false
Third, the production-only default: n8n only traces production executions by default, so manual editor runs produce nothing unless you set N8N_OTEL_TRACES_PRODUCTION_ONLY=false. This is the most common reason a new setup looks broken during testing, and it keeps editor experiments out of production dashboards.
For long-running workflows, enable start spans on n8n 2.44.0 or later: n8n then sends a 0 ms child span the moment the workflow or node starts, using N8N_OTEL_TRACES_EMIT_WORKFLOW_START_SPAN and N8N_OTEL_TRACES_EMIT_NODE_START_SPAN, so a workflow waiting on approval is visible instead of invisible until it ends. For agent runs, n8n 2.33.0 or later adds agent tracing with N8N_AGENTS_TRACING_ENABLED=true: one root span per run, one execute_tool span per tool call. It records prompts and tool results by default, so set N8N_AGENTS_TRACING_RECORD_INPUTS=false and N8N_AGENTS_TRACING_RECORD_OUTPUTS=false if traces must not carry model inputs or outputs.
Finally, protect the connection. If your collector needs authentication, set N8N_OTEL_EXPORTER_OTLP_HEADERS to comma-separated key=value pairs, or use the _FILE variant, N8N_OTEL_EXPORTER_OTLP_HEADERS_FILE, so the token never sits in a compose file. To export over gRPC on 2.39.0 or later, set N8N_OTEL_EXPORTER_OTLP_PROTOCOL=grpc and point the endpoint at port 4317 explicitly: a gRPC endpoint without a port connects to 443 by default, not 4317.
Step 8: Build the slow-node habit
Tracing earns its keep when it changes how you investigate. Once a week, sort workflow traces by duration and filter to the slowest 5 percent. For each slow workflow, open its slowest node span and note the node type, the duration, and whether it carries an error. A slow HTTP Request node usually means a slow downstream API, fixed with a timeout, retry policy, or cache; a slow Code node usually means an inefficient loop or large payload. Pair this habit with our n8n error handling guide so surfaced failures get retried or escalated instead of merely observed, as the n8n OpenTelemetry announcement describes for end-to-end propagation.
Troubleshooting
No traces appear in Jaeger at all
Check the endpoint first: the most common mistake is pointing N8N_OTEL_EXPORTER_OTLP_ENDPOINT at http://host:4318/v1/traces, which n8n extends to a doubled path. Point it at the base URL, http://host:4318, and restart n8n. Next, check the port against the protocol: HTTP uses 4318, gRPC uses 4317, and a gRPC endpoint without an explicit port silently connects to 443. In queue mode, confirm the variables are set on every instance. Finally, run Send test trace on 2.27.0 or later: failure means connectivity or authentication, success with no executions means sampling or the production-only default.
Test runs from the editor produce no traces
This is the production-only default doing its job: n8n only traces production executions, so manual editor runs are invisible by design. Either activate the workflow and trigger it through its real webhook or schedule, or set N8N_OTEL_TRACES_PRODUCTION_ONLY=false while testing and restore the default afterwards, or editor experiments will pollute your latency dashboards.
Only workflow spans appear, with no node detail
Check N8N_OTEL_TRACES_INCLUDE_NODE_SPANS: if it is false, n8n exports only workflow.execute spans, a legitimate cost saving that removes the per-node detail needed for slow-node debugging. Set it back to true, restart n8n if you set it by environment variable, and run the workflow again. Also check sampling: a sampled-out trace produces nothing at all, unlike a workflow-only trace.
Crashed executions are missing from error-rate dashboards
Before n8n 2.42.0, crashed executions produced no span, so trace-based error rates missed every platform failure, such as an out-of-memory kill or a container eviction. From 2.42.0, a crashed execution produces one workflow.execute span with status crashed. Update alerts that match only error to include crashed, since stalled queue jobs changed status in the same release. Treat a crashed span’s duration as detection lag, not run time: exclude crashed spans from latency metrics and count them in error-rate metrics only.
Authenticated collectors reject the export
Check the header format: N8N_OTEL_EXPORTER_OTLP_HEADERS takes comma-separated key=value pairs, and a malformed entry can break the whole export. Prefer the _FILE variant for tokens, so the secret never sits in a process listing or a version-controlled compose file. For gRPC, n8n lowercases header keys into gRPC metadata, skips entries gRPC rejects, and logs a warning instead of failing startup, so check the n8n log first when a gRPC export authenticates differently from an HTTP export.
FAQ
Which n8n version do I need for native OpenTelemetry tracing?
Version 2.19.0 or later, where tracing shipped as a preview. Version 2.27.0 adds the Settings > OpenTelemetry interface, 2.39.0 adds gRPC export, and 2.44.0 adds start spans for long-running workflows.
Do I need an OpenTelemetry Collector, or can I send traces straight to Jaeger?
Jaeger accepts OTLP directly, so a local Jaeger instance is enough at first. Add a collector when you route traces to several backends, add authentication, or transform attributes before export.
Why do I only see workflow spans and no node spans?
Node spans follow the Include node spans setting, backed by N8N_OTEL_TRACES_INCLUDE_NODE_SPANS. If it is false, n8n exports only workflow.execute spans. Turn it on, restart n8n if you set it by environment variable, and rerun the workflow.
Can I trace a request end to end from my app through n8n and back out?
Yes. If the inbound webhook request carries a W3C traceparent header, the workflow span becomes a child of that trace, and HTTP Request nodes can inject traceparent into outbound calls so downstream services continue the same trace ID.
How do I keep tracing costs down on a busy instance?
Set N8N_OTEL_TRACES_SAMPLE_RATE to 0.1 to export 10 percent of traces, and N8N_OTEL_TRACES_INCLUDE_NODE_SPANS to false for workflow-level spans only. Tracing already defaults to production executions only.
Does n8n capture workflow inputs and outputs in traces?
Workflow and node spans record metadata such as workflow name, node type, status, duration, and input and output item counts, not payload contents. Agent tracing is the exception: it records prompts and tool results by default, disableable with N8N_AGENTS_TRACING_RECORD_INPUTS and N8N_AGENTS_TRACING_RECORD_OUTPUTS.
Conclusion
Native OpenTelemetry tracing turns n8n from a black box into one more service in your observability stack. Start with Jaeger on your laptop, prove the first trace with a three-node workflow, then carry the same two environment variables into production with sampling and workflow-only spans as cost controls. Then wire traces into your team’s backend, and add the slow-node review to your weekly routine. For the wider picture, see our complete n8n guide and safe update guide.
Frequently asked questions
Which n8n version do I need for native OpenTelemetry tracing?
Version 2.19.0 or later, where tracing shipped as a preview. Version 2.27.0 adds the Settings > OpenTelemetry interface, 2.39.0 adds gRPC export, and 2.44.0 adds start spans for long-running workflows.
Do I need an OpenTelemetry Collector, or can I send traces straight to Jaeger?
Jaeger accepts OTLP directly, so a local Jaeger instance is enough at first. Add a collector when you route traces to several backends, add authentication, or transform attributes before export.
Why do I only see workflow spans and no node spans?
Node spans follow the Include node spans setting, backed by N8N_OTEL_TRACES_INCLUDE_NODE_SPANS. If it is false, n8n exports only workflow.execute spans. Turn it on, restart n8n if you set it by environment variable, and rerun the workflow.
Can I trace a request end to end from my app through n8n and back out?
Yes. If the inbound webhook request carries a W3C traceparent header, the workflow span becomes a child of that trace, and HTTP Request nodes can inject traceparent into outbound calls so downstream services continue the same trace ID.
How do I keep tracing costs down on a busy instance?
Set N8N_OTEL_TRACES_SAMPLE_RATE to 0.1 to export 10 percent of traces, and N8N_OTEL_TRACES_INCLUDE_NODE_SPANS to false for workflow-level spans only. Tracing already defaults to production executions only.
Does n8n capture workflow inputs and outputs in traces?
Workflow and node spans record metadata such as workflow name, node type, status, duration, and input and output item counts, not payload contents. Agent tracing is the exception: it records prompts and tool results by default, disableable with N8N_AGENTS_TRACING_RECORD_INPUTS and N8N_AGENTS_TRACING_RECORD_OUTPUTS.