ADR-012: Use OpenTelemetry and a Self-Hostable LGTM Stack for Observability
Status
Accepted
Date
2026-08-26
Context
Chilarai needs observability across its runtime: the NestJS API and the Next.js web app (including its Backend-For-Frontend proxy). The platform must capture:
- Standard request telemetry
- Distributed traces across web → BFF → API → database
- Exceptions
- Custom business events
- Custom metrics
The platform targets both:
- Multi-tenant SaaS deployments
- Self-hosted school (on-premises) deployments
This mirrors the constraints in prior decisions. As with ADR-006 (self-hosted Keycloak over Azure AD B2C) and ADR-008 (MinIO over Azure Blob), the observability solution must not lock the platform into a single cloud, and must work for on-premises deployments where a cloud-managed backend is unavailable.
The solution should:
- Be open source
- Be vendor neutral at the instrumentation layer
- Be self-hostable and cloud portable
- Run easily in containers
- Avoid vendor lock-in
- Work for both SaaS and on-premises deployments
- Support traces, metrics, and logs, correlated by trace context
- Provide retention long enough for real debugging (target: at least 90 days for traces and exceptions)
- Avoid unnecessary operational burden where a managed option is acceptable
Decision Drivers
- Instrumentation should be independent of any specific backend
- The backend should be swappable per deployment without changing application code
- Self-hosting must remain possible for on-premises deployments
- Retention and cost must remain controllable
- Secrets and PII must never be captured in telemetry
Decision
Chilarai will use OpenTelemetry (OTel) for instrumentation and a self-hostable LGTM stack (Loki, Grafana, Tempo, Mimir) as the default observability backend.
- Both applications are instrumented with OpenTelemetry for traces, metrics, and logs.
- Applications export OTLP to an OpenTelemetry Collector. The Collector
is the vendor-neutral boundary — analogous to the
StorageProviderandIdentityProviderabstractions — and is the single point for batching, sampling, and redaction. - The default backend is a self-hostable LGTM stack: Tempo (traces), Loki (logs), Mimir (metrics), and Grafana (query, dashboards, alerting).
- Local development uses the
grafana/otel-lgtmall-in-one container plus a Collector. - Custom events are modelled as metrics when they are aggregatable (e.g. login counts) and as structured logs with trace context when they are discrete, auditable occurrences (e.g. tenant switch).
- The trace ID is the correlation ID; it is injected into structured logs so logs, traces, and metrics correlate in Grafana.
- Secrets and PII (Authorization headers, cookies, tokens, emails) are redacted at both the instrumentation and Collector layers.
Deployment position (current)
The initial deployed footprint will self-host OTLP (Collector + LGTM containers). The choice of a managed backend is deferred: whether to continue self-hosting or adopt Grafana Cloud or Azure Monitor will be evaluated later. Because applications only ever speak OTLP to the Collector, this decision requires only a change to the Collector's exporter — no application changes.
Rationale
Vendor Neutrality
OpenTelemetry and OTLP are open standards. Application instrumentation does not depend on any backend. The backend can change without touching business code.
Self-Hostable and Cloud Portable
The LGTM components are open source and container-friendly (Docker, Kubernetes, VMs). They run on any cloud or on-premises, consistent with the platform's self-hosting goals.
Works for SaaS and On-Premises
A SaaS control plane can point the Collector at a managed or self-hosted backend. An on-premises school deployment runs the same containers locally. Both use identical instrumentation.
Retention and Cost Control
Self-hosted LGTM stores cold data in object storage, making long retention cheap and configurable. This satisfies the retention requirement without the per-GB ingestion cost of a managed monitor. Managed options remain available where their convenience outweighs their cost.
Consistent with Existing Decisions
This reuses the established pattern: an open, self-hostable default behind a provider abstraction, with managed alternatives available but not required.
Alternatives Considered
Option 1: OpenTelemetry + Self-Hostable LGTM (Selected)
Vendor-neutral instrumentation, self-hostable and portable backend, managed options available via the Collector. Requires operating the stack when self-hosted.
Option 2: Azure Monitor / Application Insights
Fully managed, 90-day default retention for Application Insights, and Entra integration. Not selected as the default because it is Azure-locked and cannot serve on-premises deployments, and its per-GB ingestion cost is comparatively high. Retained as an optional managed OTLP exporter for Azure-hosted deployments.
Option 3: Grafana Cloud
Managed LGTM running the same open-source software as the self-hosted default, with an OTLP endpoint. Not selected as the default only to avoid committing to a managed vendor prematurely. Retained as an optional managed backend, reachable without code changes because it is the same software.
Option 4: Proprietary SaaS (Datadog, New Relic)
Rich features, but proprietary agents, strong vendor lock-in, and high cost at scale. Rejected as inconsistent with the platform's portability goals.
Option 5: AWS CloudWatch + X-Ray
AWS-native, but a fragmented traces/logs/metrics experience and AWS lock-in. Rejected as a default for the same portability reasons.
Consequences
Positive
- Open source and vendor neutral
- Self-hostable and cloud portable
- Works for both SaaS and on-premises deployments
- Backend is swappable per deployment with no application changes
- Traces, metrics, and logs correlate through trace context
- Long, cost-controlled retention when self-hosted
- Managed options (Grafana Cloud, Azure Monitor) remain available
Negative
- Self-hosting the stack in production is an operational responsibility
- Running a Collector adds a component to each environment
- Redaction and sampling must be configured and maintained
- The managed-backend decision is deferred and must be revisited
Summary
Chilarai instruments with OpenTelemetry and exports OTLP to a Collector, with a self-hostable LGTM stack as the default backend. This keeps the platform open, portable, and viable for both SaaS and on-premises deployments, while leaving the managed-backend choice (self-host, Grafana Cloud, or Azure Monitor) as a deferred, code-free decision.