You need an endpoint, a token, and a few naming conventions. Then pick the template that matches how your application runs. A Python/FastAPI service needs no code changes.
| Production (via tunnel) | https://otel.<domain>, OTLP HTTP (http/protobuf) only |
| Private network / same host | <host>:4317 (gRPC) or <host>:4318 (HTTP) |
| Auth | Authorization: Bearer <OTLP_AUTH_TOKEN> (ask the stack operator) |
The tunnel routes HTTPS to the collector's HTTP receiver only. Set
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf when you send through it. gRPC
works on private paths only (VPN, WireGuard, or the same Docker network).
Never expose 4317/4318 directly.
service.nameis required: one stable name per deployable unit (r9lab-api, notr9lab-api-prod-2). Dashboards key on it.projectandenvare both required resource attributes:projectis the namebootstrap.shwas run with,envisprod,staging, ordev. Together they are what every alert and dashboard filters on, and a sender that omits them is counted asunknownand alerts as an uncovered project.- Keep labels low-cardinality. Prometheus turns every distinct label value into a series. User IDs, request IDs, and timestamps belong in the log line or in span attributes, not in resource attributes or metric labels.
- Loki indexes only the identity labels:
service.name,department,project,env,host.name(the list is inconfig/loki.yaml). Every other attribute is structured metadata. Select the stream first, then filter:{service_name="my-service", env="prod"} | service_instance_id="...".
The Service Health dashboard and the error-rate alert read the standard HTTP
server metrics (http_server_request_duration_seconds). The
auto-instrumentation below emits them.
pip install opentelemetry-distro opentelemetry-exporter-otlp opentelemetry-instrumentation-fastapiexport OTEL_SERVICE_NAME=my-service
export OTEL_RESOURCE_ATTRIBUTES=project=my-project,env=prod
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.example.org
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <token>"
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
export OTEL_SEMCONV_STABILITY_OPT_IN=http
opentelemetry-instrument uvicorn app:app --host 0.0.0.0 --port 8000That gives traces, RED metrics, and logs with trace context, with no OTel
code in the app. A working example is this repo's demo/ service
plus compose.demo.yml.
Every OpenTelemetry SDK reads the same four environment variables:
OTEL_SERVICE_NAME=my-service
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.example.org
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <token>"
OTEL_RESOURCE_ATTRIBUTES=project=my-project,env=prodAn application cannot report other containers' stdout, host resources, or its
own crash loops. One Grafana Alloy agent per host ships those, over the same
endpoint and token. Run ./bootstrap.sh <project> <env> on the monitoring
host and follow what it prints. Details are in
templates/README.md.