{
  "schema_version": 2,
  "id": "operate/iris/identity-service/configuration",
  "title": "Configure the Identity Service",
  "url": "https://redis.io/docs/latest/operate/iris/identity-service/configuration/",
  "summary": "Configure the Identity Service metadata Redis, tenant, service registration, agent-key settings, and TLS.",
  "tags": [
    "docs",
    "operate",
    "iris"
  ],
  "last_updated": "2026-11-03T16:45:14-08:00",
  "page_type": "content",
  "content_hash": "aedcbe82b527ff5108b81f5bf1dd5748889f0cf4877df1314ac59ffb9dfa7249",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "You configure the Identity Service with the Helm values of its own release, in `values-ids.yaml`. No service chart reads these values or checks them against its own, so keep the Secret names, Service addresses, and tenant consistent across releases yourself.\n\nThe Identity Service reads its configuration only when it starts. A changed value rolls the pod at `helm upgrade`. A changed Secret that you own doesn't roll the pod unless you also change its checksum value. See [Rotate credentials](https://redis.io/docs/latest/operate/iris/identity-service/operations#rotate-credentials).\n\nTo see every value and its default, run:\n\n[code example]"
    },
    {
      "id": "metadata-redis",
      "title": "Metadata Redis",
      "role": "content",
      "text": "The metadata Redis holds the hashed agent keys, their grants, and subject grants. If you lose it, every agent loses access.\n\n| Value | Default | Meaning |\n| --- | --- | --- |\n| `metadata.existingSecret` | `\"\"` | Required. The Secret that holds the metadata Redis connection. |\n| `metadata.secretKey` | `metadata.yaml` | The key in that Secret. |\n| `metadata.existingSecretChecksum` | `\"\"` | Optional. Change it to roll the pod after you edit the Secret. |\n\nThe Secret's key holds a configuration fragment:\n\n[code example]\n\n- The Identity Service always uses database 0. It ignores a `/<db>` index in the URL.\n- Don't change the host after the first install. The Identity Service then reads an empty database, and every key stops working. To move the data, follow [Move the metadata Redis](https://redis.io/docs/latest/operate/iris/identity-service/operations#move-the-metadata-redis).\n- Turn on persistence (append-only file or RDB snapshots) and keep the data directory on durable storage. Without persistence, a Redis restart deletes every key."
    },
    {
      "id": "control-token",
      "title": "Control token",
      "role": "content",
      "text": "| Value | Default | Meaning |\n| --- | --- | --- |\n| `control.existingSecret` | `\"\"` | Your own Secret with the control token. |\n| `control.secretKey` | `token` | The key in that Secret. |\n| `control.autoGenerate` | `true` | Generate `<release>-iris-identity-service-control-token` when `existingSecret` is empty. |\n| `control.existingSecretChecksum` | `\"\"` | Optional. Change it to roll the pod after you edit your Secret. |\n\nThe control token always works, also when operator sign-in is on. Give only a small group access to its Secret. For what it can do, see [Credentials](https://redis.io/docs/latest/operate/iris/identity-service/authentication#credentials).\n\nFor GitOps tools such as Argo CD or Flux, supply the control token with `control.existingSecret`. These tools render the chart without cluster access, so a generated token changes on every render."
    },
    {
      "id": "tenant",
      "title": "Tenant",
      "role": "content",
      "text": "| Value | Default | Meaning |\n| --- | --- | --- |\n| `runtime.apiKeyTenant.singleTenantID` | `\"\"` | The one tenant of the installation, for example `default`. |\n\nSet `singleTenantID` to the same value as the `tenantId` of every service release. The Identity Service adds this tenant to each grant that names no tenant, and rejects a grant that names another tenant with `400`.\n\nA tenant ID is 1 to 256 characters, without `|` or control characters. Matching is exact and case-sensitive. For a numeric ID, quote it in YAML or set it with `--set-string`, so Helm keeps it as a string. You can't change the tenant after you mint keys: keys, grants, and service data keep the old ID."
    },
    {
      "id": "register-services",
      "title": "Register services",
      "role": "content",
      "text": "Each service needs two entries on the Identity Service release:\n\n- A runtime credential in `runtime.serviceCredentials`, so the service's data plane can check agent keys.\n- A grant validator in `productValidation.products.<product>`, so the Identity Service can check the service's resources before it stores a grant.\n\nThis table lists the entries for each service, with the release names and Secret names used in the associated deployment guide:\n\n| Service | `serviceCredentials` `name` and `subject` | `allowedProducts` | Validator key | Validator `baseURL` | Runtime credential Secret | Internal token Secret |\n| --- | --- | --- | --- | --- | --- | --- |\n| Agent Memory | `memory-dp` | `[memory]` | `memory` | `http://redis-agent-memory-controlplane:9100` | `memory-dp-credential` | `memory-internal-token` |\n| Agent Playbooks | `playbook-dp` | `[playbook]` | `playbook` | `http://playbook-controlplane:9200` | `playbook-dp-credential` | `playbook-controlplane-internal` |\n| Context Gateway | `context-gateway-dp`, subject `context-gateway-dataplane` | `[context-gateway]` | `context-gateway` | `http://gateway-redis-context-gateway-controlplane:9100` | `gateway-runtime` | `gateway-grant-validation` |\n| Intent Router | `intent-dp` | `[intent]` | `intent` | `http://intent-redis-intent-router-controlplane:9400` | `intent-dp-credential` | `intent-controlplane-internal` |\n| Lens | `lens-dataplane` | `[lens]` | `lens` | `http://lens-redis-lens-controlplane:9300` | `lens-dp-credential` | `lens-internal-token` |\n\nThe runtime credential Secret is the same Secret that the service's data plane mounts, and the internal token Secret is the same Secret that its control plane mounts. If you install a service under another release name or `fullnameOverride`, change the validator address to match. For the service-side values, see each service's deploy guide."
    },
    {
      "id": "serve-several-services-from-one-release",
      "title": "Serve several services from one release",
      "role": "content",
      "text": "One release serves every service. Write all entries in one values file. Helm replaces lists instead of merging them, so each `helm upgrade` must restate every `serviceCredentials` entry, not only the new one.\n\nThis example serves Agent Memory and Intent Router:\n\n[code example]\n\nTo add a service to a running release:\n\n1. Install the service, or note its control-plane Service and Secret names.\n1. Add the service's runtime credential and validator to `values-ids.yaml`, next to the existing entries.\n1. Run `helm upgrade ids redis-ai/iris-identity-service --version 1.0.0 -n <namespace-name> -f values-ids.yaml`.\n1. Check that `GET /v1/products` lists the new service.\n\nEvery service on one release uses the same tenant."
    },
    {
      "id": "runtime-credential-fields",
      "title": "Runtime credential fields",
      "role": "content",
      "text": "| Field | Meaning |\n| --- | --- |\n| `name` | Required. Unique, 1 to 44 lowercase letters, digits, and dashes, with no trailing dash. It also names the mount directory. |\n| `subject` | Required. The identity the data plane authenticates as. |\n| `allowedOperations` | Required. One or more of the operations in the next table. |\n| `allowedProducts` | Required. The services whose keys this credential may check. |\n| `existingSecret`, `secretKey` | The shared Secret and its key (default `token`). |\n| `existingSecretChecksum` | Optional. Change it to roll the pod after you edit the Secret. |\n| `autoGenerate`, `generatedSecretName` | For development only. The chart generates the token instead of reading your Secret. |\n\n| Operation | Use |\n| --- | --- |\n| `api-key-introspect` | Check an agent key. Every data plane needs it. |\n| `subject-grant-lookup` | Read subject grants. Context Gateway needs it when IdP sign-in for agents is on. See [Subject grants](https://redis.io/docs/latest/operate/iris/identity-service/authentication#subject-grants). |\n| `auth-me` | Let the caller check its own credential. |\n| `auth-introspect` | An earlier delegated token check. Don't use it for new installations. |"
    },
    {
      "id": "grant-validator-fields",
      "title": "Grant validator fields",
      "role": "content",
      "text": "| Field | Meaning |\n| --- | --- |\n| `enabled` | Default `true`. A disabled entry renders nothing. |\n| `baseURL` | Required. An absolute `http://` or `https://` URL of the service's control plane, with no user information, query, or fragment. |\n| `credential.existingSecret`, `credential.secretKey` | Required. The control plane's internal token Secret and its key (default `token`). |\n| `credential.existingSecretChecksum` | Optional. Change it to roll the pod after you edit the Secret. |\n\nThe Identity Service calls the validator with a 5-second timeout and doesn't follow redirects. If the control plane doesn't answer, minting fails with `424`.\n\n`runtime.unscopedGrantsProduct` names the service that owns grants stored before grants carried a service. Set it to `memory` when the release serves Agent Memory. When set, it must name an enabled validator."
    },
    {
      "id": "agent-keys-and-the-key-cache",
      "title": "Agent keys and the key cache",
      "role": "content",
      "text": "| Value | Default | Meaning |\n| --- | --- | --- |\n| `apiKeys.maxRotateGraceSeconds` | `604800` (7 days) | The longest time an old key can stay valid after a rotation. |\n| `apiKeys.userBinding.enabled` | `false` | Lets a mint bind a key to a user with `userId`. Agent Memory uses it for owner isolation. |\n| `runtime.cache.softTtlSeconds` | `180` | When a data plane refreshes a cached key check. Must not exceed `hardTtlSeconds`. |\n| `runtime.cache.hardTtlSeconds` | `300` | The longest time a data plane uses a cached key check. A revoked key can keep working for up to this time. |"
    },
    {
      "id": "operator-sign-in-and-subject-grants",
      "title": "Operator sign-in and subject grants",
      "role": "content",
      "text": "| Value | Default | Meaning |\n| --- | --- | --- |\n| `controlAuth.oidc` | Not set | Operator sign-in to the control API with access tokens from your identity provider (IdP). |\n| `controlAuth.roleBindings` | Not set | Maps IdP group or role claims to Identity Service roles. Requires `controlAuth.oidc.enabled`. |\n| `controlAuth.workloadIdentity` | `{}` | Lets a Kubernetes workload call the control API with its service account token. |\n| `subjectGrants.allowed` | `[]` | The grant shapes that subject grants may have. Empty allows none. |\n\nSee [Authentication and authorization](https://redis.io/docs/latest/operate/iris/identity-service/authentication) for how to set them."
    },
    {
      "id": "tls-fips-and-network",
      "title": "TLS, FIPS, and network",
      "role": "content",
      "text": "- **Private certificate authority (CA).** To reach a `rediss://` metadata Redis, an `https://` validator, or an IdP signed by a private CA, put the PEM bundle in a Secret and set `tls.caCertSecret`. The key defaults to `ca.crt` (`tls.caCertKey`). The Identity Service trusts the bundle in addition to the system CAs. The chart has no option to skip certificate verification.\n\n  [code example]\n\n- **Federal Information Processing Standards (FIPS).** Set `security.profile: fips`. The Identity Service then refuses to start unless the metadata Redis URL is `rediss://` and every validator `baseURL` is `https://`.\n- **Network.** The chart deploys no NetworkPolicy and no Ingress. Let only the service planes, your operators, and your automation reach port `9200`. The health routes need no credential, so don't expose them outside the cluster.\n- **Another namespace.** If the Identity Service runs in another namespace than a service, use fully qualified Service names, such as `http://redis-agent-memory-controlplane.<product-namespace>.svc:9100`, and create each shared Secret in both namespaces with the same value."
    },
    {
      "id": "availability-and-resources",
      "title": "Availability and resources",
      "role": "content",
      "text": "| Value | Default | Meaning |\n| --- | --- | --- |\n| `replicaCount` | `1` | Identity Service replicas. The service keeps its state in the metadata Redis. |\n| `resources` | Requests `100m` CPU and `128Mi` memory; limits `500m` CPU and `512Mi` memory | Container resources. |\n| `nodeSelector`, `affinity`, `tolerations` | Empty | Pod scheduling. |\n| `image.repository`, `image.tag` | `redislabs/iris-identity-service`, the chart version | Set them to pull from a private registry. |\n| `imagePullSecrets` | `[]` | Pull Secrets for a private registry. |\n| `podDisruptionBudget.enabled` | `false` | Add a PodDisruptionBudget with `maxUnavailable: 1` when `replicaCount` is above `1`. |\n\nReadiness checks the metadata Redis, and liveness doesn't. When the metadata Redis is unreachable, the pod turns unready and stops receiving traffic, but Kubernetes doesn't restart it. Readiness doesn't check the service control planes, so one service's outage doesn't take the Identity Service out of service."
    },
    {
      "id": "configuration-overlays",
      "title": "Configuration overlays",
      "role": "configuration",
      "text": "- **`configData`** adds Identity Service settings that have no Helm value. The chart owns the credential, service, tenant, cache, and key settings, and refuses credential wiring in `configData`. Set operator policy either in `controlAuth` or in `configData.auth`, never both.\n- **Policy overlays** are Secrets that hold more configuration, such as role bindings, named by `secrets.secretName` and `secrets.additionalSecrets`. Each Secret holds the overlay under the key `overlay.yaml` (`secrets.overlayKey`). The Identity Service loads them in order, after the metadata Secret. An overlay can't change the control token, the runtime credentials, or the validators. Change `secrets.checksum` to roll the pod after you edit an overlay Secret.\n\nRole bindings in overlays merge by issuer, claim value, scope, and service. A later binding replaces a matching one, and a binding with `role: none` removes it."
    }
  ],
  "examples": [
    {
      "id": "overview-ex0",
      "language": "bash",
      "code": "helm show values redis-ai/iris-identity-service --version 1.0.0",
      "section_id": "overview"
    },
    {
      "id": "metadata-redis-ex0",
      "language": "yaml",
      "code": "metadata:\n  urls: [\"rediss://<metadata-user>:<metadata-password>@<metadata-host>:<metadata-port>\"]",
      "section_id": "metadata-redis"
    },
    {
      "id": "serve-several-services-from-one-release-ex0",
      "language": "yaml",
      "code": "runtime:\n  unscopedGrantsProduct: memory\n  apiKeyTenant:\n    singleTenantID: default\n  serviceCredentials:\n    - name: memory-dp\n      subject: memory-dp\n      allowedOperations: [api-key-introspect]\n      allowedProducts: [memory]\n      existingSecret: memory-dp-credential\n      secretKey: token\n    - name: intent-dp\n      subject: intent-dp\n      allowedOperations: [api-key-introspect]\n      allowedProducts: [intent]\n      existingSecret: intent-dp-credential\n      secretKey: token\nproductValidation:\n  products:\n    memory:\n      baseURL: http://redis-agent-memory-controlplane:9100\n      credential:\n        existingSecret: memory-internal-token\n        secretKey: token\n    intent:\n      enabled: true\n      baseURL: http://intent-redis-intent-router-controlplane:9400\n      credential:\n        existingSecret: intent-controlplane-internal\n        secretKey: token",
      "section_id": "serve-several-services-from-one-release"
    },
    {
      "id": "tls-fips-and-network-ex0",
      "language": "bash",
      "code": "kubectl -n <namespace-name> create secret generic ids-ca --from-file=ca.crt=./ca.crt",
      "section_id": "tls-fips-and-network"
    }
  ]
}
