Spitfirev0.71.0 · What's new

Version: 0.71.0This documentation is for Spitfire 0.71.0.

Connecting a .NET service with OTel

OpenTelemetry .NET Automatic Instrumentation collects traces, metrics and logs without touching the code. Values and general rules: Connecting services with OTel: overview.

Version table

Runtime Version Note
.NET 8, 9, 10 1.18.0 Linux, Windows, Docker
.NET 6, 7 1.9.0 .NET 6 and 7 support was removed in 1.10.0; stay on 1.9.0
.NET Framework 4.6.2 – 4.8.1 1.18.0 Windows only (IIS or Windows service)
.NET Framework 4.6.1 and earlier (4.5 included) not supported Upgrade to 4.6.2+ (e.g. 4.8) or take server metrics only: Collector

Find your version: for .NET dotnet --list-runtimes; for .NET Framework the targetFramework in the application's web.config or .csproj file.

The commands below say v1.18.0; if your band is .NET 6/7, write v1.9.0 everywhere.

Verified: 2026-11-02. Source: supported runtimes, changelog (.NET 6/7 removed in 1.10.0), 1.9.0 documentation, IIS, Windows service.

What you need

  • Spitfire address https://<spitfire> and OTLP receiver token sfotlp_…
  • Tower address http://<tower>:4318 and Collector token sfcol_…
  • Protocol http/protobuf
  • Service name <service-name>, version <service-version>, environment <environment>
  • With a private CA, the CA file /etc/spitfire-otel/ca.pem (Windows: C:\spitfire-otel\ca.pem)
  • With a proxy, the Tower and Spitfire in NO_PROXY
  • Sampling parentbased_traceidratio, 0.1

Linux (systemd)

  1. Download and run the install script (the machine needs curl and unzip):
bash
curl -sSfL -O https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/v1.18.0/otel-dotnet-auto-install.sh
sudo OTEL_DOTNET_AUTO_HOME=/opt/otel-dotnet-auto SKIP_RELEASE_VERIFICATION=true sh ./otel-dotnet-auto-install.sh
sudo mkdir -p /opt/spitfire-otel /etc/spitfire-otel
Note

If the machine has the GitHub CLI (gh), delete SKIP_RELEASE_VERIFICATION=true; the script then verifies the downloaded package's signature. 1.9.0 does not use this variable.

  1. Create the file /etc/spitfire-otel/<service-name>.env and paste this:
bash
IS_OTEL_ENABLED=true
OTEL_SERVICE_NAME=<service-name>
OTEL_RESOURCE_ATTRIBUTES=service.version=<service-version>,deployment.environment=<environment>
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://<spitfire>/otlp/v1/traces
OTEL_EXPORTER_OTLP_TRACES_HEADERS=authorization=sfotlp_…
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://<spitfire>/otlp/v1/logs
OTEL_EXPORTER_OTLP_LOGS_HEADERS=authorization=sfotlp_…
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://<tower>:4318/v1/metrics
OTEL_EXPORTER_OTLP_METRICS_HEADERS=authorization=sfcol_…
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1
NO_PROXY=<tower>,<spitfire>
#OTEL_EXPORTER_OTLP_CERTIFICATE=/etc/spitfire-otel/ca.pem
  1. Restrict its permission: sudo chmod 600 /etc/spitfire-otel/<service-name>.env
  2. Create the file /opt/spitfire-otel/otel-run and paste this:
bash
#!/bin/sh
# IS_OTEL_ENABLED=true: the profiler and startup hook are loaded; any other value: neither is loaded.
if [ "$IS_OTEL_ENABLED" = "true" ]; then
  export OTEL_DOTNET_AUTO_HOME=/opt/otel-dotnet-auto
  . /opt/otel-dotnet-auto/instrument.sh
else
  export OTEL_SDK_DISABLED=true
fi
exec "$@"
  1. Make it executable: sudo chmod 755 /opt/spitfire-otel/otel-run
  2. Find the service's current start command: systemctl cat <unit> | grep ^ExecStart
  3. Open sudo systemctl edit <unit> and paste this (on the last line, replace the command after ExecStart= with the command from step 6):
ini
[Service]
EnvironmentFile=/etc/spitfire-otel/<service-name>.env
ExecStart=
ExecStart=/opt/spitfire-otel/otel-run /usr/bin/dotnet /srv/app/App.dll
  1. Restart: sudo systemctl daemon-reload && sudo systemctl restart <unit>
  2. See that it is loaded: log files named with the service's process number appear under ls /var/log/opentelemetry/dotnet.
Warning

If the service starts with DOTNET_EnableDiagnostics=0, the profiler is not loaded on .NET 8+; remove that line.

Docker

  1. Put the otel-run file (Linux step 4) next to the Dockerfile.
  2. Add this to the Dockerfile (the image needs curl and unzip); /opt/spitfire-otel/otel-run goes at the start of ENTRYPOINT, the rest is your command:
dockerfile
ADD --chmod=755 https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/v1.18.0/otel-dotnet-auto-install.sh /tmp/otel-dotnet-auto-install.sh
RUN OTEL_DOTNET_AUTO_HOME=/opt/otel-dotnet-auto SKIP_RELEASE_VERIFICATION=true sh /tmp/otel-dotnet-auto-install.sh && rm /tmp/otel-dotnet-auto-install.sh
COPY --chmod=755 otel-run /opt/spitfire-otel/otel-run
ENTRYPOINT ["/opt/spitfire-otel/otel-run", "dotnet", "/app/App.dll"]
  1. Save the environment file (Linux step 2) on the Docker machine as /etc/spitfire-otel/<service-name>.env.
  2. Run: docker run --env-file /etc/spitfire-otel/<service-name>.env … or, in compose, under the service env_file: /etc/spitfire-otel/<service-name>.env.
  3. When the environment file changes, recreate the container (restart is not enough): docker compose up -d --force-recreate <service>

Kubernetes

  1. Prepare the image as in the Docker section.
  2. Save the environment file (Linux step 2) as <service-name>.env and make it a Secret:
bash
kubectl create secret generic <service-name>-otel --from-env-file=<service-name>.env \
  --dry-run=client -o yaml | kubectl apply -f -
  1. In the Deployment, add this under the container:
yaml
envFrom:
  - secretRef:
      name: <service-name>-otel
  1. Restart: kubectl rollout restart deployment/<deployment>

Windows: installing (once)

  1. Open PowerShell as administrator and run:
powershell
New-Item -ItemType Directory -Force C:\spitfire-otel | Out-Null
Invoke-WebRequest -Uri https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/v1.18.0/OpenTelemetry.DotNet.Auto.psm1 -OutFile C:\spitfire-otel\OpenTelemetry.DotNet.Auto.psm1
Import-Module C:\spitfire-otel\OpenTelemetry.DotNet.Auto.psm1
Install-OpenTelemetryCore -SkipReleaseVerification
Note

If the machine has the GitHub CLI (gh.exe), delete -SkipReleaseVerification; the package's signature is then verified. 1.9.0 has no such parameter; delete it.

  1. If the application runs in IIS go to "Windows: IIS", if it is a Windows service go to "Windows: service".

Windows: IIS

  1. Run once in an administrator PowerShell (IIS restarts):
powershell
Import-Module C:\spitfire-otel\OpenTelemetry.DotNet.Auto.psm1
Register-OpenTelemetryForIIS
  1. For the application's application pool, write the pool's name (IIS Manager → Application Pools) in place of <app-pool> and run:
powershell
Import-Module WebAdministration
$Pool = "<app-pool>"
$IS_OTEL_ENABLED = "true"
$Vars = [ordered]@{
  IS_OTEL_ENABLED = $IS_OTEL_ENABLED
  OTEL_SERVICE_NAME = "<service-name>"
  OTEL_RESOURCE_ATTRIBUTES = "service.version=<service-version>,deployment.environment=<environment>"
  OTEL_EXPORTER_OTLP_PROTOCOL = "http/protobuf"
  OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = "https://<spitfire>/otlp/v1/traces"
  OTEL_EXPORTER_OTLP_TRACES_HEADERS = "authorization=sfotlp_…"
  OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = "https://<spitfire>/otlp/v1/logs"
  OTEL_EXPORTER_OTLP_LOGS_HEADERS = "authorization=sfotlp_…"
  OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = "http://<tower>:4318/v1/metrics"
  OTEL_EXPORTER_OTLP_METRICS_HEADERS = "authorization=sfcol_…"
  OTEL_TRACES_EXPORTER = "otlp"
  OTEL_METRICS_EXPORTER = "otlp"
  OTEL_LOGS_EXPORTER = "otlp"
  OTEL_TRACES_SAMPLER = "parentbased_traceidratio"
  OTEL_TRACES_SAMPLER_ARG = "0.1"
  NO_PROXY = "<tower>,<spitfire>"
}
# Private CA: $Vars.OTEL_EXPORTER_OTLP_CERTIFICATE = "C:\spitfire-otel\ca.pem"
if ($IS_OTEL_ENABLED -ne "true") {
  $Vars.COR_ENABLE_PROFILING = "0"
  $Vars.CORECLR_ENABLE_PROFILING = "0"
  $Vars.OTEL_SDK_DISABLED = "true"
  $Vars.OTEL_TRACES_EXPORTER = "none"
  $Vars.OTEL_METRICS_EXPORTER = "none"
  $Vars.OTEL_LOGS_EXPORTER = "none"
}
$Filter = "system.applicationHost/applicationPools/add[@name='$Pool']/environmentVariables"
foreach ($Name in @($Vars.Keys) + @("COR_ENABLE_PROFILING", "CORECLR_ENABLE_PROFILING", "OTEL_SDK_DISABLED")) {
  Remove-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter $Filter -Name "." -AtElement @{ name = $Name } -ErrorAction SilentlyContinue
}
foreach ($Name in $Vars.Keys) {
  Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter $Filter -Name "." -Value @{ name = $Name; value = $Vars[$Name] }
}
Restart-WebAppPool -Name $Pool
  1. Open a few pages of the site; w3wp log files appear under C:\ProgramData\OpenTelemetry .NET AutoInstrumentation\logs.
Warning

Register-OpenTelemetryForIIS affects every application pool on the machine. For the pools you do not want collected, run step 2 with $IS_OTEL_ENABLED = "false".

Windows: service

  1. In an administrator PowerShell, write the service's name (Name in the Get-Service list) in place of <windows-service> and run:
powershell
Import-Module C:\spitfire-otel\OpenTelemetry.DotNet.Auto.psm1
$Service = "<windows-service>"
$IS_OTEL_ENABLED = "true"
$Otel = @(
  "IS_OTEL_ENABLED=$IS_OTEL_ENABLED",
  "OTEL_SERVICE_NAME=<service-name>",
  "OTEL_RESOURCE_ATTRIBUTES=service.version=<service-version>,deployment.environment=<environment>",
  "OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf",
  "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://<spitfire>/otlp/v1/traces",
  "OTEL_EXPORTER_OTLP_TRACES_HEADERS=authorization=sfotlp_…",
  "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://<spitfire>/otlp/v1/logs",
  "OTEL_EXPORTER_OTLP_LOGS_HEADERS=authorization=sfotlp_…",
  "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://<tower>:4318/v1/metrics",
  "OTEL_EXPORTER_OTLP_METRICS_HEADERS=authorization=sfcol_…",
  "OTEL_TRACES_EXPORTER=otlp",
  "OTEL_METRICS_EXPORTER=otlp",
  "OTEL_LOGS_EXPORTER=otlp",
  "OTEL_TRACES_SAMPLER=parentbased_traceidratio",
  "OTEL_TRACES_SAMPLER_ARG=0.1",
  "NO_PROXY=<tower>,<spitfire>"
)
# Private CA: $Otel += "OTEL_EXPORTER_OTLP_CERTIFICATE=C:\spitfire-otel\ca.pem"
if ($IS_OTEL_ENABLED -ne "true") { $Otel += "OTEL_SDK_DISABLED=true" }
$Key = "HKLM:\SYSTEM\CurrentControlSet\Services\$Service"
$Keep = @((Get-ItemProperty $Key).Environment | Where-Object { $_ -and $_ -notmatch '^(IS_OTEL_ENABLED|OTEL_(?!DOTNET_AUTO_)|NO_PROXY)=' })
Set-ItemProperty $Key -Name Environment -Type MultiString -Value ($Keep + $Otel)
if ($IS_OTEL_ENABLED -eq "true") {
  Register-OpenTelemetryForWindowsService -WindowsServiceName $Service -OTelServiceName "<service-name>"
} else {
  Unregister-OpenTelemetryForWindowsService -WindowsServiceName $Service
}
  1. Log files named after the service's process appear under C:\ProgramData\OpenTelemetry .NET AutoInstrumentation\logs.

Turning off and removing

  1. Turning off: set IS_OTEL_ENABLED=false in the environment file (in the script on Windows) and restart the service (Linux step 8, Docker step 5, Kubernetes step 4, or run the Windows script again with "false").
  2. Removing: on Linux sudo systemctl revert <unit> and sudo rm -r /opt/otel-dotnet-auto /opt/spitfire-otel /etc/spitfire-otel; in Docker, delete the lines added to the Dockerfile; on Windows Unregister-OpenTelemetryForIIS or Unregister-OpenTelemetryForWindowsService -WindowsServiceName <windows-service>, then Uninstall-OpenTelemetryCore, and delete C:\spitfire-otel.

Common problems

Symptom Cause Fix
The log folder is empty The profiler was not loaded Is IS_OTEL_ENABLED true; did Register-OpenTelemetryForIIS run for IIS; is DOTNET_EnableDiagnostics=0 set
A .NET 6/7 service does not start with 1.18.0 or sends no data 1.18.0 does not support .NET 6/7 Reinstall with 1.9.0
No data from a .NET Framework 4.5 application Below 4.6.2 is not supported Move the application to 4.6.2+
"CLR profiler was not correctly loaded" on Linux ARM64 The ARM64 profiler needs glibc 2.33+; Debian 11 (e.g. the .NET 6 6.0 images) has 2.31 Use an Ubuntu 22.04+ base (e.g. the 6.0-jammy image) or an x64 server
gh not found error 1.18.0's signature check needs the GitHub CLI Install gh or use SKIP_RELEASE_VERIFICATION=true / -SkipReleaseVerification
No traces or metrics arrive Address, token or network Follow the Verifying steps