Portal Community

Enabling Loki Multi-Tenancy

# loki-config.yaml — enable multi-tenancy
auth_enabled: true       # REQUIRED for tenant isolation in production

# With auth_enabled: true:
# - Every ingest request MUST include X-Scope-OrgID: <tenant-id>
# - Every query request MUST include X-Scope-OrgID: <tenant-id>
# - Loki stores each tenant's chunks in a separate keyspace
# - Tenant A cannot query Tenant B's logs — enforced at the Loki storage layer

OTel Collector — Sending Tenant ID to Loki

# otel-collector-config.yaml — pass tenant ID as Loki header
exporters:
  loki:
    endpoint: http://loki:3100/loki/api/v1/push
    headers:
      # Forward the tenant ID from the OTel resource attribute:
      X-Scope-OrgID: "${TENANT_ID}"

# For multi-tenant deployments where one collector handles multiple tenants:
# Use the routing connector to route each tenant's logs to a separate Loki pipeline
# with the correct X-Scope-OrgID header.

# Alternative: Use the tenant processor:
processors:
  tenant:
    from_attribute: tenant.id    # Read from OTel resource attribute
    # Loki exporter automatically uses this as X-Scope-OrgID

Grafana — Tenant-Scoped Loki Data Source

# grafana-provisioning/datasources/loki.yaml — per-tenant data source
datasources:
  # Platform-wide Loki data source (no tenant scope — admin use only)
  - name: Loki-All
    type: loki
    url: http://loki:3100
    # No X-Scope-OrgID — used by platform admins only (Admin role required)

  # Tenant-specific data source (restricted to one tenant's data)
  - name: Loki-TenantABC
    type: loki
    url: http://loki:3100
    jsonData:
      httpHeaderName1: "X-Scope-OrgID"
    secureJsonData:
      httpHeaderValue1: "tenant-abc"
    # Assign to the tenant-abc team in Grafana access control
    # This data source only returns logs for tenant-abc

Label-Based Stream Isolation

# Even with auth_enabled: true, validate that your label strategy enforces isolation.
# All BizFirstGO log streams must include tenant_id as a label:

# Correct (tenant_id label present):
{job="processengine", tenant_id="tenant-abc", environment="production"}

# Incorrect (no tenant_id label — all tenants' logs visible to anyone querying this stream):
{job="processengine", environment="production"}

# Verify tenant_id is always present by checking Loki label values:
curl -G "http://localhost:3100/loki/api/v1/label/tenant_id/values" \
  -H "X-Scope-OrgID: tenant-abc"
# Should only return "tenant-abc" — never other tenant IDs
auth_enabled: false Is Only For Development

With auth_enabled: false (the default), Loki uses a fixed "fake" tenant — all logs are stored together and any query returns data from all tenants. This is fine for single-tenant development but is a critical security misconfiguration in production multi-tenant deployments. Always set auth_enabled: true in production.