.NET on a Linux VM
Zero-code instrumentation for .NET applications running on Linux virtual machines.
This guide instruments a .NET application (.NET 6 or later) on a Linux VM with OpenTelemetry .NET automatic instrumentation. You don't change application code.
Prerequisites
- A Linux VM with the .NET runtime installed
curlandunzip- The GitHub CLI (
gh), signed in. The installer uses it to verify the downloaded release (see Step 1) - Network access from the VM to the KubeSense trace endpoint on port
33443. See Instrumentation.
Step 1: Install the automatic instrumentation
Install it into a shared directory, so the application's service user can read it.
The installer verifies the downloaded release with the GitHub CLI, which must be signed in. Sign in once with gh auth login, or provide a GitHub token in the GH_TOKEN environment variable:
curl -sSfL -o otel-dotnet-auto-install.sh \
https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/latest/download/otel-dotnet-auto-install.sh
sudo GH_TOKEN="$GH_TOKEN" OTEL_DOTNET_AUTO_HOME=/opt/otel-dotnet-auto sh ./otel-dotnet-auto-install.sh
sudo chmod +x /opt/otel-dotnet-auto/instrument.shInstalling without verification: If you can't install or sign in to the GitHub CLI on the VM, add SKIP_RELEASE_VERIFICATION=true to the install command. The installer then prints a warning and installs the release without verifying it:sudo SKIP_RELEASE_VERIFICATION=true OTEL_DOTNET_AUTO_HOME=/opt/otel-dotnet-auto sh ./otel-dotnet-auto-install.sh
The script detects the CPU architecture and C library (glibc or musl) and downloads the matching build. Check which profiler folder was installed:
ls /opt/otel-dotnet-auto
# linux-x64 (glibc, x86_64), linux-arm64, linux-musl-x64 or linux-musl-arm64Step 2: Configure and start the application
Option A: from a shell
instrument.sh sets all the profiler variables (CORECLR_*, DOTNET_*) for the current shell:
export OTEL_DOTNET_AUTO_HOME=/opt/otel-dotnet-auto
export OTEL_SERVICE_NAME=orders-api
export OTEL_EXPORTER_OTLP_ENDPOINT=http://<KUBESENSE_INGEST_HOST>:33443
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_RESOURCE_ATTRIBUTES=kubesense.cluster=<CLUSTER_NAME>,kubesense.env_type=legacy
export OTEL_METRICS_EXPORTER=none
export OTEL_LOGS_EXPORTER=none
. /opt/otel-dotnet-auto/instrument.sh
dotnet /srv/orders-api/OrdersApi.dllOption B: as a systemd service (recommended for servers)
Add the variables to the service unit, for example /etc/systemd/system/orders-api.service. Use the profiler folder you saw in Step 1. The example below is for linux-x64:
[Service]
ExecStart=/usr/bin/dotnet /srv/orders-api/OrdersApi.dll
# OpenTelemetry .NET automatic instrumentation
Environment=OTEL_DOTNET_AUTO_HOME=/opt/otel-dotnet-auto
Environment=CORECLR_ENABLE_PROFILING=1
Environment=CORECLR_PROFILER={918728DD-259F-4A6A-AC2B-B85E1B658318}
Environment=CORECLR_PROFILER_PATH=/opt/otel-dotnet-auto/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so
Environment=DOTNET_STARTUP_HOOKS=/opt/otel-dotnet-auto/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll
# Exporter settings
Environment=OTEL_SERVICE_NAME=orders-api
Environment=OTEL_EXPORTER_OTLP_ENDPOINT=http://<KUBESENSE_INGEST_HOST>:33443
Environment=OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
Environment=OTEL_RESOURCE_ATTRIBUTES=kubesense.cluster=<CLUSTER_NAME>,kubesense.env_type=legacy
Environment=OTEL_METRICS_EXPORTER=none
Environment=OTEL_LOGS_EXPORTER=noneReload and restart the service:
sudo systemctl daemon-reload
sudo systemctl restart orders-apiwarning: Set the profiler variables only on the services you want to instrument, never globally (for example in /etc/environment). Global settings instrument every .NET process on the VM.
Step 3: Verify
Generate some traffic, then open APM → Traces in KubeSense and filter by your service name. Instrumentation logs are written to /var/log/opentelemetry/dotnet. Set OTEL_LOG_LEVEL=debug temporarily for more detail.
Troubleshooting
| Symptom | Check |
|---|---|
| No traces and no instrumentation logs | CORECLR_PROFILER_PATH points to a file that doesn't exist. Use the folder installed in Step 1 |
| Export errors in the instrumentation logs | Check that the VM can reach <KUBESENSE_INGEST_HOST>:33443, and that trace ingestion is enabled on KubeSense |
| Permission errors on start | The service user can't read /opt/otel-dotnet-auto. Check its permissions |
| Traces appear under the wrong cluster | Check kubesense.cluster and kubesense.env_type in OTEL_RESOURCE_ATTRIBUTES |