docs/guides/overrides.md
The User Overrides API provides a RESTful interface for managing tenant-specific limit overrides at runtime without requiring manual edits to the runtime configuration file or service restarts.
Cortex is a multi-tenant system that applies resource limits to each tenant to prevent any single tenant from using too many resources. These limits can be configured globally via the limits section in the main configuration file, or per-tenant via the runtime_config file.
Traditionally, updating per-tenant limits required:
reload-period)The User Overrides API simplifies this process by providing HTTP endpoints that allow authorized users or systems to:
The overrides module runs as a service within Cortex and provides three main capabilities:
The overrides module must be explicitly enabled in Cortex. It is not included in the all target by default.
# Run only the overrides module
cortex -target=overrides -runtime-config.file=runtime.yaml
# Include overrides with other modules
cortex -target=overrides,query-frontend,querier -runtime-config.file=runtime.yaml
The runtime configuration file controls which limits can be modified via the API and sets upper bounds (hard limits) for tenant overrides.
# file: runtime.yaml
# Current tenant overrides
overrides:
tenant1:
ingestion_rate: 50000
max_global_series_per_user: 1000000
# Limits that can be modified via the API
api_allowed_limits:
- ingestion_rate
- ingestion_burst_size
- max_global_series_per_user
- max_global_series_per_metric
- ruler_max_rules_per_rule_group
- ruler_max_rule_groups_per_tenant
Hard limits prevent tenants from setting overrides above a specified maximum value:
# file: runtime.yaml
# Current tenant overrides
overrides:
tenant1:
ingestion_rate: 50000
max_global_series_per_user: 500000
# Allowed limits that can be modified via API
api_allowed_limits:
- ingestion_rate
- ingestion_burst_size
- max_global_series_per_user
- max_global_series_per_metric
- ruler_max_rules_per_rule_group
- ruler_max_rule_groups_per_tenant
# Hard limits (maximum values) per tenant
hard_overrides:
tenant1:
ingestion_rate: 100000
max_global_series_per_user: 2000000
tenant2:
ingestion_rate: 200000
max_global_series_per_user: 5000000
The overrides module uses the same storage backend as the runtime config. Configure it using the runtime-config section:
runtime_config:
period: 10s
file: runtime.yaml
# For S3 backend
backend: s3
s3:
bucket_name: cortex-runtime-config
endpoint: s3.amazonaws.com
access_key_id: ${AWS_ACCESS_KEY_ID}
secret_access_key: ${AWS_SECRET_ACCESS_KEY}
# For GCS backend
# backend: gcs
# gcs:
# bucket_name: cortex-runtime-config
# For filesystem backend (default)
# backend: filesystem
# filesystem:
# dir: /etc/cortex
All endpoints require authentication using the X-Scope-OrgID header with the tenant ID.
GET /api/v1/user-overrides
X-Scope-OrgID: tenant1
Returns the current overrides for the authenticated tenant in JSON format.
Response (200 OK):
{
"ingestion_rate": 50000,
"max_global_series_per_user": 500000,
"ruler_max_rules_per_rule_group": 100
}
Response (404 Not Found): If the tenant has no overrides configured, an empty object is returned:
{}
POST /api/v1/user-overrides
X-Scope-OrgID: tenant1
Content-Type: application/json
{
"ingestion_rate": 75000
}
Sets or updates specific overrides for the authenticated tenant. This operation merges with existing overrides rather than replacing them entirely.
Merge Behavior Example:
Current state:
{
"ingestion_rate": 50000,
"max_global_series_per_user": 500000,
"ruler_max_rules_per_rule_group": 100
}
Request:
{
"ingestion_rate": 75000
}
Result:
{
"ingestion_rate": 75000,
"max_global_series_per_user": 500000,
"ruler_max_rules_per_rule_group": 100
}
Response (200 OK): Returns success with no body.
Response (400 Bad Request):
api_allowed_limits)DELETE /api/v1/user-overrides
X-Scope-OrgID: tenant1
Removes all overrides for the authenticated tenant. The tenant will revert to using global default values.
Response (200 OK): Returns success with no body.
The api_allowed_limits configuration in the runtime config file controls which limits can be modified via the API. This provides an additional security layer to prevent unauthorized modification of critical limits.
api_allowed_limits:
- ingestion_rate
- ingestion_burst_size
- max_global_series_per_user
- max_global_series_per_metric
- ruler_max_rules_per_rule_group
- ruler_max_rule_groups_per_tenant
When a POST request is made:
api_allowed_limitsExample - Rejected Request:
Runtime config:
api_allowed_limits:
- ingestion_rate
- max_global_series_per_user
Request:
{
"ingestion_rate": 50000,
"max_series_per_query": 100000
}
Response (400 Bad Request):
the following limits cannot be modified via the overrides API: max_series_per_query
Limit names correspond to fields in the limits_config section. Common examples include:
ingestion_rateingestion_burst_sizemax_global_series_per_usermax_global_series_per_metricmax_local_series_per_usermax_local_series_per_metricmax_series_per_querymax_samples_per_queryruler_max_rules_per_rule_groupruler_max_rule_groups_per_tenantmax_label_names_per_seriesmax_label_name_lengthmax_label_value_lengthHard limits provide per-tenant upper bounds for override values. They prevent tenants from setting limits that exceed their allocated capacity.
Hard limits are inclusive - if a hard limit is set to 100000, then values up to and including 100000 are allowed, but 100001 and above will be rejected.
Hard limits are specified per tenant in the hard_overrides section:
hard_overrides:
tenant1:
ingestion_rate: 100000
max_global_series_per_user: 2000000
tenant2:
ingestion_rate: 500000
max_global_series_per_user: 10000000
When a POST request is made:
Example - Allowed Request (at hard limit):
Runtime config:
hard_overrides:
tenant1:
ingestion_rate: 100000
max_global_series_per_user: 2000000
Request:
{
"ingestion_rate": 100000
}
Response (200 OK): The request succeeds because 100000 equals the hard limit (inclusive).
Example - Rejected Request (exceeds hard limit):
Runtime config:
hard_overrides:
tenant1:
ingestion_rate: 100000
max_global_series_per_user: 2000000
Request:
{
"ingestion_rate": 100001
}
Response (400 Bad Request):
limit ingestion_rate exceeds hard limit: 100001 > 100000
| Configuration | Purpose | Scope |
|---|---|---|
Default limits (limits_config) | Global defaults for all tenants | All tenants |
Overrides (overrides) | Per-tenant custom limits | Specific tenants |
Hard limits (hard_overrides) | Maximum allowed override values (inclusive) | Specific tenants |
Hierarchy:
Default Limits (global)
↓
Tenant Overrides (per-tenant, must be ≤ hard limits)
↓
Hard Limits (per-tenant maximum, inclusive)
Set initial overrides for a new tenant:
curl -X POST http://cortex:8080/api/v1/user-overrides \
-H "X-Scope-OrgID: tenant1" \
-H "Content-Type: application/json" \
-d '{
"ingestion_rate": 50000,
"max_global_series_per_user": 1000000,
"ruler_max_rules_per_rule_group": 50
}'
Update only the ingestion rate while preserving other overrides:
curl -X POST http://cortex:8080/api/v1/user-overrides \
-H "X-Scope-OrgID: tenant1" \
-H "Content-Type: application/json" \
-d '{
"ingestion_rate": 75000
}'
Result: ingestion_rate updated to 75000, other limits remain unchanged.
curl -X GET http://cortex:8080/api/v1/user-overrides \
-H "X-Scope-OrgID: tenant1"
Response:
{
"ingestion_rate": 75000,
"max_global_series_per_user": 1000000,
"ruler_max_rules_per_rule_group": 50
}
curl -X DELETE http://cortex:8080/api/v1/user-overrides \
-H "X-Scope-OrgID: tenant1"
Result: Tenant reverts to global default limits.
Attempt to set a disallowed limit:
curl -X POST http://cortex:8080/api/v1/user-overrides \
-H "X-Scope-OrgID: tenant1" \
-H "Content-Type: application/json" \
-d '{
"ingestion_rate": 50000,
"some_invalid_limit": 100
}'
Response (400):
the following limits cannot be modified via the overrides API: some_invalid_limit
Attempt to exceed hard limit:
curl -X POST http://cortex:8080/api/v1/user-overrides \
-H "X-Scope-OrgID: tenant1" \
-H "Content-Type: application/json" \
-d '{
"ingestion_rate": 100001
}'
Response (400):
limit ingestion_rate exceeds hard limit: 100001 > 100000
Note: If the hard limit is 100000, then 100000 itself would be allowed (hard limits are inclusive), but 100001 exceeds it.
X-Scope-OrgID headerapi_allowed_limits to restrict which limits can be modifiedhard_overrides to enforce maximum values per tenantruntime-config.periodhard_overrides to prevent runaway resource usageapi_allowed_limits that are safe for self-serviceThis means the tenant has no overrides configured. This is normal for new tenants. An empty JSON object {} is returned.
The runtime config is reloaded periodically based on runtime-config.period (default: 10s). Changes may take up to this duration to be applied.
This indicates an issue reading or parsing the runtime configuration file. Check: