SigNoz
Docs
PricingCustomers
Get Started - Free
Docs
IntroductionContributingMigrate from DatadogSigNoz API
OpenTelemetry
What is OpenTelemetryOpenTelemetry Collector GuideOpenTelemetry Demo
Community
Support
Slack
X
Launch Week
Changelog
Dashboard Templates
DevOps Wordle
Newsletter
KubeCon, Atlanta 2025
More
SigNoz vs DatadogSigNoz vs New RelicSigNoz vs GrafanaSigNoz vs Dynatrace
Careers
AboutTermsPrivacySecurity & Compliance
SigNoz Logo
SigNoz
All systems operational
HIPAASOC-2
SigNoz Cloud - This page applies to SigNoz Cloud editions.
Self-Host - This page applies to self-hosted SigNoz editions.

Rust OpenTelemetry Instrumentation Guide

This guide shows you how to instrument your Rust application with OpenTelemetry and send traces to SigNoz. Learn how to auto-instrument frameworks like Axum, Actix Web, reqwest, and tonic, or add manual instrumentation for custom spans and attributes.

Most steps are identical. To adapt this guide, update the endpoint and remove the ingestion key header as shown in Cloud → Self-Hosted.

Prerequisites

  • Rust 1.75 or newer
  • Cargo (comes with Rust)
  • A SigNoz Cloud account or self-hosted SigNoz instance

Tested with OpenTelemetry Rust SDK v0.31.0.

Send traces to SigNoz

A VM is a virtual computer that runs on physical hardware. This includes:

  • Cloud VMs: AWS EC2, Google Compute Engine, Azure VMs, DigitalOcean Droplets
  • On-premise VMs: VMware, VirtualBox, Hyper-V, KVM
  • Bare metal servers: Physical servers running Linux/Unix directly

Use this section if you're deploying your Rust application directly on a server or VM without containerization.

Step 1. Set environment variables

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_RESOURCE_ATTRIBUTES="service.version=<service-version>"

Verify these values:

  • <region>: Your SigNoz Cloud region
  • <your-ingestion-key>: Your SigNoz ingestion key
  • <service-name>: A descriptive name for your service (e.g., payment-service)
  • <service-version> (optional): Your release version, image tag, or git SHA (e.g., 1.4.2, a01dbef8).

Set service.version to a per-build value, not a static string. SigNoz detects a deployment each time this value changes. Common sources:

  • Bash / shell: service.version=$(git rev-parse --short HEAD)
  • GitHub Actions: service.version=${{ github.sha }}
  • GitLab CI: service.version=$CI_COMMIT_SHORT_SHA
  • Kubernetes: inject from your Helm chart image tag or CI variable

Step 2. Add OpenTelemetry dependencies

Add the following to your Cargo.toml under [dependencies]:

Cargo.toml
opentelemetry = { version = "0.31.0", features = ["trace"] }
opentelemetry_sdk = { version = "0.31.0", features = ["trace", "rt-tokio"] }
opentelemetry-otlp = { version = "0.31.0", features = ["http-proto", "trace", "reqwest-client", "tls-roots"] }
tokio = { version = "1", features = ["full"] }

The tls-roots and reqwest-client features in opentelemetry-otlp are required for sending data to SigNoz Cloud over HTTPS.

Step 3. Create the tracer initialization

Add the following imports and function to your main.rs:

src/main.rs
use opentelemetry_otlp::{SpanExporter, WithHttpConfig};
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;

fn init_tracer() -> SdkTracerProvider {
    // Resource::builder() reads OTEL_SERVICE_NAME (via SdkProvidedResourceDetector)
    // and OTEL_RESOURCE_ATTRIBUTES (via EnvResourceDetector) automatically.
    // The exporter reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS.
    let exporter = SpanExporter::builder()
        .with_http()
        .build()
        .expect("Failed to create span exporter");

    let resource = Resource::builder().build();

    SdkTracerProvider::builder()
        .with_resource(resource)
        .with_batch_exporter(exporter)
        .build()
}

Then initialize it in main():

src/main.rs
let tracer_provider = init_tracer();
opentelemetry::global::set_tracer_provider(tracer_provider.clone());

Call tracer_provider.shutdown() before your process exits to flush buffered spans.

Step 4. Run your application

cargo run

The steps above configure the trace exporter but does not auto-instrument anything out of the box. You can either add framework instrumentation for automatic spans from libraries like Axum, Actix Web, and reqwest, or use manual instrumentation to create custom spans, attributes, and events.

Step 1. Set environment variables in your deployment

Add these environment variables to your Kubernetes 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_RESOURCE_ATTRIBUTES
  value: 'service.version=<service-version>'

Verify these values:

  • <region>: Your SigNoz Cloud region
  • <your-ingestion-key>: Your SigNoz ingestion key
  • <service-name>: A descriptive name for your service (e.g., payment-service)
  • <service-version> (optional): Your release version, image tag, or git SHA (e.g., 1.4.2, a01dbef8).

Set service.version to a per-build value, not a static string. SigNoz detects a deployment each time this value changes. Common sources:

  • Bash / shell: service.version=$(git rev-parse --short HEAD)
  • GitHub Actions: service.version=${{ github.sha }}
  • GitLab CI: service.version=$CI_COMMIT_SHORT_SHA
  • Kubernetes: inject from your Helm chart image tag or CI variable

Step 2. Add OpenTelemetry dependencies

Add the following to your Cargo.toml under [dependencies]:

Cargo.toml
opentelemetry = { version = "0.31.0", features = ["trace"] }
opentelemetry_sdk = { version = "0.31.0", features = ["trace", "rt-tokio"] }
opentelemetry-otlp = { version = "0.31.0", features = ["http-proto", "trace", "reqwest-client", "tls-roots"] }
tokio = { version = "1", features = ["full"] }

Step 3. Create the tracer initialization

Add the following imports and function to your main.rs:

src/main.rs
use opentelemetry_otlp::{SpanExporter, WithHttpConfig};
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;

fn init_tracer() -> SdkTracerProvider {
    // Resource::builder() reads OTEL_SERVICE_NAME (via SdkProvidedResourceDetector)
    // and OTEL_RESOURCE_ATTRIBUTES (via EnvResourceDetector) automatically.
    // The exporter reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS.
    let exporter = SpanExporter::builder()
        .with_http()
        .build()
        .expect("Failed to create span exporter");

    let resource = Resource::builder().build();

    SdkTracerProvider::builder()
        .with_resource(resource)
        .with_batch_exporter(exporter)
        .build()
}

Then initialize it in main():

src/main.rs
let tracer_provider = init_tracer();
opentelemetry::global::set_tracer_provider(tracer_provider.clone());

Call tracer_provider.shutdown() before your process exits to flush buffered spans.

Step 4. Run your application

Build your container image and deploy it to your Kubernetes cluster.

The steps above configure the trace exporter but does not auto-instrument anything out of the box. You can either add framework instrumentation for automatic spans from libraries like Axum, Actix Web, and reqwest, or use manual instrumentation to create custom spans, attributes, and events.

Step 1. Set environment variables

Pass environment variables 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_RESOURCE_ATTRIBUTES="service.version=<service-version>" \
    your-image:latest

Or add them to your Dockerfile:

Dockerfile
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_RESOURCE_ATTRIBUTES="service.version=<service-version>"

Verify these values:

  • <region>: Your SigNoz Cloud region
  • <your-ingestion-key>: Your SigNoz ingestion key
  • <service-name>: A descriptive name for your service (e.g., payment-service)
  • <service-version> (optional): Your release version, image tag, or git SHA (e.g., 1.4.2, a01dbef8).

Set service.version to a per-build value, not a static string. SigNoz detects a deployment each time this value changes. Common sources:

  • Bash / shell: service.version=$(git rev-parse --short HEAD)
  • GitHub Actions: service.version=${{ github.sha }}
  • GitLab CI: service.version=$CI_COMMIT_SHORT_SHA
  • Kubernetes: inject from your Helm chart image tag or CI variable

Step 2. Add OpenTelemetry dependencies

Add the following to your Cargo.toml under [dependencies]:

Cargo.toml
opentelemetry = { version = "0.31.0", features = ["trace"] }
opentelemetry_sdk = { version = "0.31.0", features = ["trace", "rt-tokio"] }
opentelemetry-otlp = { version = "0.31.0", features = ["http-proto", "trace", "reqwest-client", "tls-roots"] }
tokio = { version = "1", features = ["full"] }

Step 3. Create the tracer initialization

Add the following imports and function to your main.rs:

src/main.rs
use opentelemetry_otlp::{SpanExporter, WithHttpConfig};
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;

fn init_tracer() -> SdkTracerProvider {
    // Resource::builder() reads OTEL_SERVICE_NAME (via SdkProvidedResourceDetector)
    // and OTEL_RESOURCE_ATTRIBUTES (via EnvResourceDetector) automatically.
    // The exporter reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS.
    let exporter = SpanExporter::builder()
        .with_http()
        .build()
        .expect("Failed to create span exporter");

    let resource = Resource::builder().build();

    SdkTracerProvider::builder()
        .with_resource(resource)
        .with_batch_exporter(exporter)
        .build()
}

Then initialize it in main():

src/main.rs
let tracer_provider = init_tracer();
opentelemetry::global::set_tracer_provider(tracer_provider.clone());

Call tracer_provider.shutdown() before your process exits to flush buffered spans.

Step 4. Run your application

Dockerfile
FROM rust:1.75 AS builder

WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/target/release/your-app .

CMD ["./your-app"]
docker build -t your-image:latest .
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_RESOURCE_ATTRIBUTES="service.version=<service-version>" \
    your-image:latest

The steps above configure the trace exporter but does not auto-instrument anything out of the box. You can either add framework instrumentation for automatic spans from libraries like Axum, Actix Web, and reqwest, or use manual instrumentation to create custom spans, attributes, and events.

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_RESOURCE_ATTRIBUTES = "service.version=<service-version>"

Verify these values:

  • <region>: Your SigNoz Cloud region
  • <your-ingestion-key>: Your SigNoz ingestion key
  • <service-name>: A descriptive name for your service (e.g., payment-service)
  • <service-version> (optional): Your release version, image tag, or git SHA (e.g., 1.4.2, a01dbef8).

Set service.version to a per-build value, not a static string. SigNoz detects a deployment each time this value changes. Common sources:

  • Bash / shell: service.version=$(git rev-parse --short HEAD)
  • GitHub Actions: service.version=${{ github.sha }}
  • GitLab CI: service.version=$CI_COMMIT_SHORT_SHA
  • Kubernetes: inject from your Helm chart image tag or CI variable

Step 2. Add OpenTelemetry dependencies

Add the following to your Cargo.toml under [dependencies]:

Cargo.toml
opentelemetry = { version = "0.31.0", features = ["trace"] }
opentelemetry_sdk = { version = "0.31.0", features = ["trace", "rt-tokio"] }
opentelemetry-otlp = { version = "0.31.0", features = ["http-proto", "trace", "reqwest-client", "tls-roots"] }
tokio = { version = "1", features = ["full"] }

Step 3. Create the tracer initialization

Add the following imports and function to your main.rs:

src/main.rs
use opentelemetry_otlp::{SpanExporter, WithHttpConfig};
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;

fn init_tracer() -> SdkTracerProvider {
    // Resource::builder() reads OTEL_SERVICE_NAME (via SdkProvidedResourceDetector)
    // and OTEL_RESOURCE_ATTRIBUTES (via EnvResourceDetector) automatically.
    // The exporter reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS.
    let exporter = SpanExporter::builder()
        .with_http()
        .build()
        .expect("Failed to create span exporter");

    let resource = Resource::builder().build();

    SdkTracerProvider::builder()
        .with_resource(resource)
        .with_batch_exporter(exporter)
        .build()
}

Then initialize it in main():

src/main.rs
let tracer_provider = init_tracer();
opentelemetry::global::set_tracer_provider(tracer_provider.clone());

Call tracer_provider.shutdown() before your process exits to flush buffered spans.

Step 4. Run your application

cargo run

The steps above configure the trace exporter but does not auto-instrument anything out of the box. You can either add framework instrumentation for automatic spans from libraries like Axum, Actix Web, and reqwest, or use manual instrumentation to create custom spans, attributes, and events.

Framework instrumentation

The setup above configures OTLP export for your application. If you use tracing, reqwest, Axum, or Actix Web, add one of the integrations below to capture spans automatically.

All four integrations below emit tracing spans. Add this bridge once to your Cargo.toml and startup code:

Cargo.toml
tracing-subscriber = { version = "0.3.20", features = ["fmt"] }
tracing-opentelemetry = "0.32.0"
src/main.rs
use opentelemetry::trace::TracerProvider;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;

let tracer = tracer_provider.tracer("my-app");
tracing_subscriber::registry()
    .with(tracing_subscriber::fmt::layer())
    .with(tracing_opentelemetry::layer().with_tracer(tracer))
    .init();

Tracing

Core tracing

Additional dependency

Cargo.toml
tracing = "0.1.41"

Minimal setup

src/main.rs
use tracing::instrument;

#[instrument(skip_all, fields(order.id = %order_id))]
async fn process_order(order_id: &str) {
    validate_order(order_id).await;
    charge_payment(order_id).await;
}
View package on crates.io
View OpenTelemetry bridge on crates.io

Validate

With your application running, verify traces are being sent to SigNoz:

  1. Trigger an action in your app that generates a trace. Execute the instrumented code path a few times.
  2. In SigNoz, open the Services tab and click Refresh. Your application should appear.
  3. Go to the Traces tab to see your application's traces.

Troubleshooting

Why don't traces appear in SigNoz?

Check environment variables are set:

echo $OTEL_EXPORTER_OTLP_ENDPOINT
echo $OTEL_SERVICE_NAME

Verify your ingestion key is correct:

echo $OTEL_EXPORTER_OTLP_HEADERS

Check for TLS errors:

Ensure you have the tls-roots and reqwest-client features enabled in opentelemetry-otlp for HTTPS endpoints:

opentelemetry-otlp = { version = "0.31.0", features = ["http-proto", "trace", "reqwest-client", "tls-roots"] }

Why do OTLP exports fail with connection refused?

  • VM: Verify the endpoint URL and that your firewall allows outbound HTTPS on port 443
  • Kubernetes: Ensure the endpoint is reachable from within your pod
  • Self-hosted: Confirm the collector is listening on the expected port (default: 4318 for HTTP)

Why are spans not being exported?

Ensure you call tracer_provider.shutdown() before your application exits. The batch exporter buffers spans and only sends them periodically or on shutdown:

// At the end of main()
tracer_provider.shutdown().expect("Failed to shutdown tracer provider");

Why do I see failed to create span exporter errors?

This usually indicates a dependency version mismatch. Ensure all OpenTelemetry crates use compatible versions:

opentelemetry = "0.31.0"
opentelemetry_sdk = "0.31.0"
opentelemetry-otlp = "0.31.0"

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 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.

Using the Collector

When using a collector, update your environment variables to point to the collector endpoint:

export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
export OTEL_SERVICE_NAME="<service-name>"
# No ingestion key needed when using a collector

You'll also need to remove the tls-roots feature if connecting over HTTP:

Cargo.toml
opentelemetry-otlp = { version = "0.31.0", features = ["http-proto", "trace", "reqwest-client"] }

See Switch from direct export to Collector for step-by-step instructions.

For more details, see Why use the OpenTelemetry Collector? and the Collector configuration guide.

Next Steps

  • Send Metrics from your Rust application to correlate traces with metrics for better observability.
  • Need to create custom spans or add attributes yourself? Use the Manual Instrumentation in Rust guide once the base setup is in place.
  • Set up alerts for your Rust application

Last updated: May 14, 2026

Edit on GitHub

Was this page helpful?

Your response helps us improve this page.

Prev
Manual Instrumentation
Next
Manual Instrumentation
On this page
Prerequisites
Send traces to SigNoz
Step 1. Set environment variables
Step 2. Add OpenTelemetry dependencies
Step 3. Create the tracer initialization
Step 4. Run your application
Step 1. Set environment variables in your deployment
Step 2. Add OpenTelemetry dependencies
Step 3. Create the tracer initialization
Step 4. Run your application
Step 1. Set environment variables
Step 2. Add OpenTelemetry dependencies
Step 3. Create the tracer initialization
Step 4. Run your application
Step 1. Set environment variables (PowerShell)
Step 2. Add OpenTelemetry dependencies
Step 3. Create the tracer initialization
Step 4. Run your application
Framework instrumentation
Validate
Troubleshooting
Why don't traces appear in SigNoz?
Why do OTLP exports fail with `connection refused`?
Why are spans not being exported?
Why do I see `failed to create span exporter` errors?
Setup OpenTelemetry Collector (Optional)
What is the OpenTelemetry Collector?
Why use it?
Using the Collector
Next Steps

Is this page helpful?

Your response helps us improve this page.