Loki’s "multi-tenancy" isn’t about isolating tenants in the traditional sense of separate instances; it’s about a single Loki instance serving data for multiple distinct users or teams, with isolation achieved through tenant IDs.
Let’s see this in action. Imagine you have two teams, "dev" and "ops," and you want their logs to be completely separate.
First, you configure Loki to expect these tenant IDs. In your loki-config.yaml:
auth_enabled: false # For simplicity, we'll disable auth and rely on client-side tenant IDs
Now, when your applications send logs, they must include the X-Scope-TenantID header.
For the "dev" team, logs would be sent like this (using curl as an example):
curl -X POST -H "X-Scope-TenantID: dev" \
-H "Content-Type: application/json" \
http://loki.example.com:3100/loki/api/v1/push \
-d '{"streams": [{"stream": {"app": "myapp", "level": "info"}, "values": [["1678886400000000000", "hello from dev"]], "timestamp": "2023-03-15T10:00:00Z"}]}'
And for the "ops" team:
curl -X POST -H "X-Scope-TenantID: ops" \
-H "Content-Type: application/json" \
http://loki.example.com:3100/loki/api/v1/push \
-d '{"streams": [{"stream": {"app": "monitoring", "level": "warn"}, "values": [["1678886460000000000", "disk usage high"]], "timestamp": "2023-03-15T10:01:00Z"}]}'
When querying, the tenant ID is also crucial. For "dev":
curl -G "http://loki.example.com:3100/loki/api/v1/query" \
-H "X-Scope-TenantID: dev" \
--data-urlencode 'query={app="myapp"}' \
--data-urlencode 'start=2023-03-15T10:00:00Z' \
--data-urlencode 'end=2023-03-15T10:01:00Z'
And for "ops":
curl -G "http://loki.example.com:3100/loki/api/v1/query" \
-H "X-Scope-TenantID: ops" \
--data-urlencode 'query={app="monitoring"}' \
--data-urlencode 'start=2023-03-15T10:00:00Z' \
--data-urlencode 'end=2023-03-15T10:01:00Z'
Notice how the same Loki instance, with the same underlying storage, is serving and retrieving data, but the tenant ID acts as a logical partition. Loki internally prefixes all object storage keys (e.g., in S3, GCS, or MinIO) with the tenant ID. This means that for tenant_id: dev, all its data objects will reside under a dev/ prefix in your object store, and similarly for ops/. This physical separation in object storage is what guarantees isolation.
The problem Loki solves with tenant IDs is enabling multiple independent users or organizations to share a single, cost-effective Loki cluster without their data intermingling. It allows for centralized management of logs while providing the illusion of separate logging systems for each tenant. This is particularly useful for SaaS providers or large organizations with distinct departments.
Internally, when Loki processes a push request, it extracts the X-Scope-TenantID header. If authentication is enabled (which it often is in production, typically via an auth.middleware like oidc or basic), the tenant ID might be derived from the authenticated user’s claims. This tenant ID is then used to scope all subsequent operations for that request, including how data is written to and read from the underlying object storage. The object store key format becomes something like tenant_id/index/tenant_id_part1/tenant_id_part2/... or tenant_id/chunks/tenant_id_part1/tenant_id_part2/.... This partitioning is fundamental to how Loki achieves multi-tenancy.
A common point of confusion is that Loki doesn’t automatically enforce tenant ID usage if auth_enabled is false. In this mode, it’s entirely up to the client to provide the X-Scope-TenantID header. If a client omits it, Loki might assign a default tenant ID (often an empty string or _ depending on configuration and version), effectively creating a "default" tenant that can see all data if other tenants don’t explicitly set their IDs. This is why in production, auth_enabled is almost always true, and the authentication middleware is responsible for determining and enforcing the tenant ID for each request.
The next step is typically exploring how to integrate actual authentication and authorization mechanisms to dynamically assign and validate tenant IDs based on user identity.