1.9 KiB
1.9 KiB
Multi-tenancy
This stack is multi-tenant: multiple teams or projects share the same Mimir, Loki, Tempo, and Grafana instances. Tenant isolation is enforced at two layers:
- Storage layer — Mimir/Loki/Tempo route reads and writes by the
X-Scope-OrgIDHTTP header. Each tenant has a unique slug. - Grafana layer — one Org per tenant. Each Org has its own datasources
that inject the tenant's
X-Scope-OrgIDautomatically.
Tenant slug rules
- Lowercase ASCII letters, digits, and dashes only.
- Max 40 characters.
- Must be unique across the deployment.
- Examples:
team-alpha,web-prod,customer-acme.
Onboarding
ansible-playbook ansible/playbooks/onboard-tenant.yml \
-e tenant_slug=team-alpha \
-e tenant_name='Team Alpha' \
-e admin_email=alpha-admin@example.com
This creates a Grafana Org named "Team Alpha", switches into it, and
provisions Mimir/Loki/Tempo datasources whose secure HTTP header
X-Scope-OrgID = team-alpha is set.
Client configuration
Clients use Grafana Alloy with the tenant slug embedded:
loki.write "default" {
endpoint {
url = "http://10.8.0.1:3100/loki/api/v1/push"
headers = { "X-Scope-OrgID" = "team-alpha" }
}
}
See config/alloy/config.alloy.example.
Offboarding
- Submit Loki delete requests for the tenant's logs:
ansible-playbook playbooks/gdpr-purge.yml -e tenant=team-alpha -e selector='{job=~".+"}' -e start=2020-01-01T00:00:00Z - Stop ingesting (revoke the tenant's WireGuard peer + remove their Alloy instance).
- Delete the Grafana Org via the Grafana API.
- Wait for retention to lapse on Mimir/Tempo (90/14 days respectively) — they have no per-tenant deletion API on this version.
Per-tenant overrides
Mimir limits, Loki retention, etc. can be customized per tenant via
runtime.yaml (Mimir) or limits_config overrides (Loki). Add per-tenant
sections under overrides: in each config — see upstream docs for the schema.