Overview
Show each customer of your platform the telemetry of their own applications inside your product. Your backend queries SigNoz with one API key for each tenant (one customer of your platform). SigNoz rejects any query for the data of a different tenant.
Your customers do not sign in to SigNoz, and your own platform telemetry never appears in a customer view.
Prerequisites
- A SigNoz Cloud workspace.
- The
signoz-adminrole, because you create roles and service accounts. - A backend service in your product that can store secrets and make HTTP requests to SigNoz.
Setup
Step 1: Create an ingestion key for each tenant
- Create an ingestion key for each tenant, for example
tenant-acme. - In Settings > Ingestion Settings, copy the key value and the key ID. Applications send data with the value. Roles and queries use the ID.
- Set the key value in the environment of each application of the tenant:
OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<tenant-ingestion-key>"Verify these values:
<region>: Your SigNoz Cloud region.<tenant-ingestion-key>: The key value of the tenant from this step.
SigNoz Cloud adds the attribute signoz.workspace.key.id to all data. Its value is the ID of the key that sent the data, and an application cannot change it.
Send the telemetry of your own platform with a different key. No tenant role can then read it.
A tenant can have more than one key, for example for staging and production. Add each key ID to the role in Step 2, and filter with signoz.workspace.key.id IN ('<key-id-1>', '<key-id-2>').
Step 2: Create a scoped role and a service account for each tenant
- Create a custom role that can read only the logs, traces, and metrics of the tenant key. Follow Scope Telemetry Access by Ingestion Key with the key ID from Step 1.
- Create a service account for the tenant, for example
tenant-acme. See Service Accounts. - Assign only the scoped role to the service account.
- Create an API key for the service account. Store it in your secrets manager with the key ID of the tenant.
To add new tenants without the SigNoz UI, you can script Step 1 and Step 2 with the SigNoz API.
Step 3: Query the data of a tenant from your backend
Send queries to /api/v5/query_range with the API key of the tenant. Each query must filter on the key ID of the tenant. This example counts the spans of each service of the tenant.
{
"start": <start-unix-ms>,
"end": <end-unix-ms>,
"requestType": "scalar",
"compositeQuery": {
"queries": [
{
"type": "builder_query",
"spec": {
"name": "A",
"signal": "traces",
"aggregations": [{ "expression": "count()" }],
"filter": { "expression": "signoz.workspace.key.id = '<tenant-key-id>'" },
"groupBy": [{ "name": "service.name", "fieldContext": "resource" }]
}
}
]
}
}curl -X POST "https://<signoz-url>/api/v5/query_range" \
-H "Content-Type: application/json" \
-H "SIGNOZ-API-KEY: <tenant-api-key>" \
-d @query.jsonVerify these values:
<start-unix-ms>and<end-unix-ms>: The time range in Unix milliseconds.<tenant-key-id>: The ingestion key ID of the tenant from Step 1.<signoz-url>: The URL of your SigNoz workspace, for exampleexample.<region>.signoz.cloud.<tenant-api-key>: The service account API key of the tenant from Step 2.
The response has one row for each service of the tenant. For more query examples, see the Traces API, Logs API, and Metrics API.
If a request has more than one query, for example a formula A/B, add the key filter to each query. If one query does not have it, SigNoz rejects the request.
Step 4: Serve the data to your UI
Keep the SigNoz calls in your backend. For each request from your frontend, the backend does these steps:
- Find the tenant of the signed-in user.
- Get the key ID and the API key of that tenant from your secrets manager.
- Build the query with the key ID of the tenant in the filter. To reuse panels from a SigNoz dashboard, see Use a SigNoz dashboard as panel templates.
- Call
/api/v5/query_rangeand return the result to your frontend.
If your backend sends a key ID that the tenant does not own, SigNoz rejects the query with 403. No data of the other tenant leaves SigNoz.
Validate
- In SigNoz, open Traces Explorer and filter with
signoz.workspace.key.id = '<tenant-a-key-id>'. The spans of tenant A appear. - Send the query from Step 3 with the API key of tenant A and the key ID of tenant A. The response is
200, and its rows match the services that you saw in step 1. - Send the same query with the key ID of tenant B. The response is
403:
{
"status": "error",
"error": {
"type": "forbidden",
"code": "authz_forbidden",
"message": "service_account/<service-account-id> is not authorized to perform traces:read on resource \"builder_query/signoz.workspace.key.id/<tenant-b-key-id>\""
}
}Use a SigNoz dashboard as panel templates
Design the panels once in a SigNoz dashboard. Your backend then runs the same panel queries for each tenant.
- In each panel, filter with a
$keyvariable, for examplesignoz.workspace.key.id IN $key. - Give the role of each tenant
readaccess to the dashboard. See Restrict Dashboard Access. - Get the dashboard with
GET /api/v2/dashboards/<dashboard-id>. The query of a panel is atdata.spec.panels.<panel-id>.spec.queries[0]. - Build the
compositeQueryfromspec.pluginof that query:
spec.plugin.kind | compositeQuery |
|---|---|
signoz/BuilderQuery | {"queries": [{"type": "builder_query", "spec": <spec.plugin.spec>}]} |
signoz/CompositeQuery | <spec.plugin.spec> as it is |
signoz/PromQLQuery or signoz/ClickHouseSQL | Not supported with a key-scoped role |
- Send the request to
/api/v5/query_rangewith the key IDs of the tenant invariables:
{
"start": <start-unix-ms>,
"end": <end-unix-ms>,
"requestType": "<panel-query-kind>",
"variables": {
"key": { "type": "custom", "value": ["<tenant-key-id>"] }
},
"compositeQuery": <composite-query>
}Verify these values:
<panel-query-kind>: The value ofdata.spec.panels.<panel-id>.spec.queries[0].kind, for examplescalar,time_series, orraw.<composite-query>: The object that you built in step 4.<tenant-key-id>: The ingestion key ID of the tenant. List more than one ID if the tenant has more than one key.
SigNoz checks the role after it puts the variable values into the filter. A key ID that the role cannot read returns 403.
Troubleshooting
403 on builder_query/*
- Likely cause: A query has no
signoz.workspace.key.idfilter, or the filter usesOR. - Fix: Add
signoz.workspace.key.id = '<tenant-key-id>'withANDto each query. - Verify: The request returns
200.
403 on builder_query/signoz.workspace.key.id/$key
- Likely cause: The request has no value for
keyinvariables. - Fix: Add
"variables": {"key": {"type": "custom", "value": ["<tenant-key-id>"]}}to the request. - Verify: The request returns
200.
400 "unsupported signal"
- Likely cause: The request wraps a
signoz/CompositeQuerypanel spec in abuilder_query. - Fix: Send the spec as
compositeQuerywithout the wrapper. See Use a SigNoz dashboard as panel templates. - Verify: The request returns
200with a result for each query and formula of the panel.
403 on promql/* or clickhouse_sql/*
- Likely cause: A key-scoped role can run only Query Builder queries.
- Fix: Write the panel as a Query Builder query.
- Verify: The request returns
200.
403 "only viewers/editors/admins can access this resource"
- Likely cause: A key-scoped role can call the query endpoint only. It cannot call
/api/v1/fields/valuesor other endpoints. - Fix: Store the values for your filter menus in your backend.
Limitations
- A key-scoped role can run Query Builder queries only. PromQL and ClickHouse SQL queries return
403. - The role admits or rejects a query. It does not add the key filter for you, so your backend must add it to every query.
- You need one ingestion key, one role, one service account, and one API key for each tenant.
- This guide is for SigNoz Cloud only. Self-hosted SigNoz has no ingestion keys.
Next Steps
- Telemetry Access Reference: how SigNoz accepts or rejects a query.
- Public Sharing in Dashboards: share a dashboard with a link when you do not need tenant isolation.
- Set Custom Attributes: add attributes such as
team.nameto filter data inside one tenant.
Get Help
If you need help with the steps in this topic, please reach out to us on SigNoz Community Slack. If you are a SigNoz Cloud user, please use in product chat support located at the bottom right corner of your SigNoz instance or contact us at cloud-support@signoz.io.