Vibe monitoring with Last9 MCP: Ask your agent to fix production issues! Setup →
Last9 Last9

Auto-Instrument Your Applications Using OTel Injector

Automatically instrument your apps on Linux with the OTel Injector, no code changes, minimal setup, and support for Java, Node.js, Python, and .NET.

Jul 29th, ‘25
Auto-Instrument Your Applications Using OTel Injector
See How Last9 Works

Unified observability for all your telemetry. Open standards. Simple pricing.

Talk to us

As distributed systems scale, maintaining manual instrumentation across services quickly becomes unsustainable. The OTel Injector addresses this by automatically attaching OpenTelemetry instrumentation to applications, no code changes needed.

This blog covers how the OTel Injector works, how it integrates with Linux environments, and how to set it up for consistent telemetry across your stack.

What is the OTel Injector?

The OpenTelemetry Injector is a host-based tool that automatically adds OpenTelemetry instrumentation to your applications, with no need to touch source code or rebuild containers.

It runs on any Linux machine and is stable enough for production use. Originally developed by Splunk, it's now part of the OpenTelemetry project.

This comes in handy when you're working across services in different languages, Java here, Python there, maybe a bit of Node.js or .NET thrown in. Instead of figuring out how to instrument each one manually, the OTel Injector handles it for you at the host level.

Key Benefits:

  • No code changes or redeployments
  • Works across Java, Node.js, Python, and .NET
  • Reliable enough for production workloads
  • Lower effort than manual instrumentation, especially in polyglot setups
💡
If you're standardizing observability across services, it also helps to validate that your instrumentation follows consistent conventions, here’s how OTel Weaver helps with that.

How the OTel Injector Works

The OTel Injector instruments applications by injecting environment variables during process startup. It detects the language runtime and applies the right configuration automatically, without modifying the app code or container image.

There are two supported methods:

1. LD_PRELOAD Hook

This approach relies on Linux's dynamic linking system. A shared library (libotelinject.so) is loaded using the LD_PRELOAD mechanism. Once active, the library:

  • Intercepts calls to getenv() from the runtime
  • Modifies or appends environment variables needed for OpenTelemetry
  • Adds the correct agent configuration for the detected language runtime

This method works for most dynamically linked applications and requires minimal system-level setup.

2. Systemd Drop-ins

For applications managed by systemd, the OTel Injector uses drop-in files to configure environment variables per service. This avoids using LD_PRELOAD and integrates cleanly with how services are typically deployed on Linux.

Examples:

Java:

JAVA_TOOL_OPTIONS=-javaagent:/usr/lib/opentelemetry/otel-javaagent.jar

Node.js:

NODE_OPTIONS=-r /usr/lib/opentelemetry/otel-js/node_modules/@opentelemetry/auto-instrumentations-node/register

.NET: Sets CORECLR_ENABLE_PROFILING, CORECLR_PROFILER, DOTNET_ADDITIONAL_DEPS, and related variables

Both options achieve the same goal: automatic instrumentation at startup. The choice depends on your deployment model and control over service configurations.

Install and Configure the OTel Injector on Linux

The opentelemetry-injector package installs everything you need to get started with automatic instrumentation: the required OpenTelemetry agents, the libotelinject.so shared library, and sample configuration files.

Install the Injector Package

Download and install the appropriate package for your Linux distribution:

Debian/Ubuntu:

# Download the package and its checksum
wget https://github.com/open-telemetry/opentelemetry-injector/releases/latest/download/opentelemetry-injector_amd64.deb
wget https://github.com/open-telemetry/opentelemetry-injector/releases/latest/download/opentelemetry-injector_amd64.deb.sha256

# Verify package integrity (recommended)
sha256sum -c opentelemetry-injector_amd64.deb.sha256

# Install the package
sudo dpkg -i opentelemetry-injector_amd64.deb

RHEL/CentOS:

# Download the package and its checksum
wget https://github.com/open-telemetry/opentelemetry-injector/releases/latest/download/opentelemetry-injector.x86_64.rpm
wget https://github.com/open-telemetry/opentelemetry-injector/releases/latest/download/opentelemetry-injector.x86_64.rpm.sha256

# Verify package integrity (recommended)
sha256sum -c opentelemetry-injector.x86_64.rpm.sha256

# Install the package
sudo rpm -i opentelemetry-injector.x86_64.rpm

Method 1: Enabling Instrumentation with LD_PRELOAD

To instrument all supported processes on the host, add the OTel Injector's shared library to the system preload list:

echo /usr/lib/opentelemetry/libotelinject.so | sudo tee -a /etc/ld.so.preload

Note: This method affects all processes on the system. It's recommended to validate changes in a staging or non-production environment before rolling out system-wide.

Method 2: Configuring systemd Services

If your services are managed by systemd, you can enable instrumentation by applying a system-wide drop-in configuration:

# Create the systemd drop-in directory
sudo mkdir -p /usr/lib/systemd/system.conf.d/

# Copy the sample configuration
sudo cp /usr/lib/opentelemetry/examples/systemd/00-otelinject-instrumentation.conf \
        /usr/lib/systemd/system.conf.d/

# Reload systemd to apply changes
sudo systemctl daemon-reload

This method allows more control by limiting instrumentation to systemd-managed services, without touching global process startup behavior.

Runtime-Specific Configurations

The package also installs default config files for each supported language runtime under /etc/opentelemetry/otelinject/. These files define the environment variables needed to activate the corresponding instrumentation agents:

Java:

# /etc/opentelemetry/otelinject/java.conf
JAVA_TOOL_OPTIONS=-javaagent:/usr/lib/opentelemetry/otel-javaagent.jar

Node.js:

# /etc/opentelemetry/otelinject/node.conf
NODE_OPTIONS=-r /usr/lib/opentelemetry/otel-js/node_modules/@opentelemetry/auto-instrumentations-node/register

Python:

# /etc/opentelemetry/otelinject/python.conf
PYTHONPATH=/usr/lib/opentelemetry/otel-python
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=

You can modify these files to customize agent behavior or override default options based on your environment.

💡
If you're sending OpenTelemetry metrics to Prometheus, this breakdown of how Prometheus 3.0 handles resource attributes is worth a read — check it out here.

Advanced Configuration Options

Once instrumentation is in place, the next step is telling the agents where to send telemetry. This is done using environment variables that control exporter settings, service metadata, and signal types.

Set Exporter and Metadata Variables

Each OpenTelemetry agent looks for a standard set of environment variables. These control the destination, protocol, and metadata for traces, metrics, and logs.

Here's a basic setup:

# Collector endpoint
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

# Export protocol (grpc or http/protobuf)
OTEL_EXPORTER_OTLP_PROTOCOL=grpc

# Service metadata
OTEL_SERVICE_NAME=my-application
OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=production

# Enable specific exporters
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp

These variables can go directly into runtime-specific files under /etc/opentelemetry/otelinject/, or be set globally depending on how you've enabled the OTel Injector.

Important: Configuration of the respective agents is supported by adding/updating the following environment variables in each of these files (any environment variable not in this list will be ignored): OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_LOGS_EXPORTER, OTEL_METRICS_EXPORTER, OTEL_RESOURCE_ATTRIBUTES, and OTEL_SERVICE_NAME.

Manage Configuration in Dynamic Environments

For dynamic setups, like containers or CI/CD pipelines, you may want to avoid hardcoding values. Instead, you can template the config files and inject environment variables at runtime.

Example: Templated Java config using environment fallbacks

cat <<EOF > /etc/opentelemetry/otelinject/java.conf
JAVA_TOOL_OPTIONS=-javaagent:/usr/lib/opentelemetry/otel-javaagent.jar
OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_COLLECTOR_ENDPOINT:-http://localhost:4317}
OTEL_SERVICE_NAME=${SERVICE_NAME:-unknown-service}
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=${ENVIRONMENT:-development}
EOF

This makes it easier to reuse the same config across environments—just set the right environment variables before startup.

Language-Specific Instrumentation

The OTel Injector supports auto-instrumentation across major language runtimes. Here's what you need to know for each.

Java

Instrumentation is handled through the JAVA_TOOL_OPTIONS environment variable, which the JVM picks up automatically. This works reliably across:

  • Spring Boot apps
  • Micronaut and Quarkus services
  • Traditional WAR-based deployments
  • Containerized Java workloads

If your application already sets JAVA_TOOL_OPTIONS, double-check for conflicts before enabling the OTel Injector.

Node.js

Node.js uses the NODE_OPTIONS flag to preload the OpenTelemetry instrumentation module. Supported setups include:

  • Express.js apps
  • NestJS services
  • PM2-managed processes
  • Serverless functions (though some runtimes may limit environment overrides)

Performance note: In most cases, the overhead is low, typically between 2–5%.

Python

Python apps require the OpenTelemetry auto-instrumentation package to be installed. The OTel Injector sets up the necessary variables automatically for:

  • Django
  • Flask
  • FastAPI
  • Celery

If you're using virtual environments, make sure the agent path is reachable within that context.

.NET

For .NET, the OTel Injector configures multiple environment variables required for CLR profiling. This includes setting:

  • CORECLR_ENABLE_PROFILING
  • CORECLR_PROFILER
  • DOTNET_STARTUP_HOOKS, among others

There's no need to wire these manually; the OTel Injector handles setup across both .NET Core and Framework applications.

💡
Now you can check if automatic instrumentation is working by pulling production logs, metrics, and traces into your local setup using Last9 MCP.

Monitoring and Troubleshooting

You'll want to make sure Opentelemetry Injector is instrumenting processes and exporting telemetry correctly. Here's how to check that everything's wired up correctly and what to look into if something breaks.

How to Verify It's Working

Start by confirming that the OTel Injector's shared library is loaded into your application process:

ldd /proc/<PID>/exe | grep otelinject

Then check if the right OpenTelemetry environment variables are present:

cat /proc/<PID>/environ | tr '\0' '\n' | grep OTEL

And finally, check your service logs for any signs that telemetry is being initialized:

journalctl -u your-service | grep -i opentelemetry

If those checks pass, you should be seeing data in your backend.

Common Issues and How to Fix Them

No data showing up

First, make sure the OpenTelemetry Collector is actually running and listening on the endpoint you've configured. Network issues or mismatched protocols (like grpc vs http/protobuf) are often the culprit. Also, confirm that the service has access to the collector and isn't blocked by firewall rules or container networking.

App crashes or fails to start

This usually points to a conflict in environment variables. For example, if JAVA_TOOL_OPTIONS is already being used elsewhere, it can override the OTel Injector's settings. Version mismatches between the instrumentation agent and the runtime can also cause failures. Logs will usually show errors around agent loading or startup hooks.

High memory usage

Auto-instrumentation adds some overhead, especially if you're capturing a lot of spans. If memory spikes after rollout, start by reducing the sampling rate. For Java, keep an eye on GC behavior; some agents allocate more aggressively during startup. Also, check your resource attributes; long strings or too many custom tags can add up fast.

Monitor Performance Impact

You can track instrumentation overhead using basic system tools:

# CPU and memory usage
top -p $(pgrep -f your-application)

# Check if the app is connecting to the collector
netstat -an | grep :4317

# Collector export metrics (if exposed)
curl http://localhost:8888/metrics | grep otelcol_exporter

Integrate with Your Existing Observability Stack

The OTel Injector can be configured to forward telemetry to any backend that supports OpenTelemetry, including Prometheus for metrics and various APM platforms for end-to-end observability across metrics, traces, and logs.

Send Metrics to Prometheus

To expose metrics for Prometheus to scrape, use the built-in Prometheus exporter:

OTEL_METRICS_EXPORTER=prometheus
OTEL_EXPORTER_PROMETHEUS_HOST=0.0.0.0
OTEL_EXPORTER_PROMETHEUS_PORT=9464

Prometheus can scrape metrics from http://<host>:9464/metrics. These settings can be added to your language-specific config files under /etc/opentelemetry/otelinject/ or passed through systemd for more control.

Exporting Telemetry to Last9

Last9 supports OTLP effortlessly. To send traces, metrics, and logs to Last9, point your OpenTelemetry agents to your project’s OTLP endpoint and set the appropriate authentication headers.

OTEL_EXPORTER_OTLP_ENDPOINT=https://api.last9.io/otlp/v1
OTEL_EXPORTER_OTLP_HEADERS="last9-api-key=your-api-key"

This setup works across all supported runtimes. Once configured, you’ll be able to view traces in real time, correlate them with metrics, and debug issues directly from the Last9 dashboard.

If you’re using multiple environments (e.g., staging and production), set OTEL_RESOURCE_ATTRIBUTES to tag telemetry with relevant metadata:

OTEL_RESOURCE_ATTRIBUTES=service.name=my-app,deployment.environment=production

When to Use the OTel Injector

The OTel Injector is built for cases where you need observability across a large, mixed set of services, fast. It works especially well when you're dealing with multiple languages, older applications, or teams that don't have the time (or access) to wire up manual instrumentation.

You'll get the most value from the OTel Injector when:

  • You're managing a polyglot environment with Java, Node.js, Python, and .NET services
  • The codebase is legacy or vendor-owned, and modifying it isn't an option
  • You want consistent instrumentation across teams without enforcing how it's done in code
  • You need to roll out telemetry quickly across dozens or hundreds of services

That said, the OTel Injector isn't always the right tool. You may want to consider manual instrumentation or SDK-based approaches when:

  • You need to add custom spans, attributes, or business-specific context
  • You're in environments where root access isn't available (e.g., certain containers or PaaS setups)
  • Your app runs on a runtime that isn't supported by the OTel Injector
  • You need precise control over what gets instrumented and how

The OTel Injector is great for bootstrapping observability across your stack, but you can always pair it with more targeted instrumentation later as your needs evolve.

Final Thoughts

The OTel Injector simplifies the rollout of observability across large, multi-language environments. It removes the need for manual instrumentation, works across standard runtimes, and is stable enough for production.

For many teams, it's the quickest way to get consistent telemetry in place, especially when dealing with legacy services or limited access to source code.

Authors
Anjali Udasi

Anjali Udasi

Helping to make the tech a little less intimidating. I

Contents

Do More with Less

Unlock high cardinality monitoring for your teams.