{
  "schema_version": 2,
  "id": "operate/iris/agent-memory/self-managed/deploy",
  "title": "Deploy Redis Agent Memory",
  "url": "https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy/",
  "summary": "Install Redis Agent Memory with chart-rendered configuration and agent-key authentication, then make one authenticated call.",
  "aliases": [
    "/develop/ai/context-engine/agent-memory/self-managed/control-plane/",
    "/develop/ai/context-engine/agent-memory/self-managed/deploy-control-plane/",
    "/operate/iris/agent-memory/self-managed/deploy-control-plane/",
    "/develop/ai/context-engine/agent-memory/self-managed/install-k8s/",
    "/develop/ai/context-engine/agent-memory/self-managed/deploy-static/",
    "/operate/iris/agent-memory/self-managed/deploy-static/"
  ],
  "tags": [
    "docs",
    "operate",
    "iris"
  ],
  "last_updated": "2026-10-06T12:03:53+03:00",
  "page_type": "content",
  "content_hash": "d5cca4585e960c7e55c0e06d46ac87b3eec63decd0865de989350fbc522da552",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "These steps install Redis Agent Memory with chart-rendered configuration and agent-key\nauthentication, and end with one authenticated call to the Data Plane.\n\nYou use three credentials along the way: the Control Plane admin token, the Identity Service\ncontrol token, and an agent key. The chart generates the first two. For what each credential\nunlocks, see [Credentials](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/authentication#credentials).\n\nBefore you begin, review the [prerequisites](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/prerequisites)."
    },
    {
      "id": "1-create-the-namespace",
      "title": "1. Create the namespace",
      "role": "content",
      "text": "[code example]"
    },
    {
      "id": "2-create-secrets",
      "title": "2. Create Secrets",
      "role": "content",
      "text": "[code example]\n\n`overlay.yaml` holds the Redis addresses and provider credentials. The Data Plane, the workers, and\nthe Control Plane all mount it:\n\n[code example]\n\n`ids-metadata.yaml` tells the Identity Service where Metadata Redis is:\n\n[code example]"
    },
    {
      "id": "prepare-worker-identity-for-your-platform",
      "title": "Prepare worker identity for your platform",
      "role": "content",
      "text": "With agent-key authentication, workers call the Data Plane with their projected service account\ntoken, and the Data Plane checks that token against the cluster's JSON Web Key Set (JWKS). The\nchart's defaults disagree here: the Data Plane defaults to `agent_key` authentication, while\n`workerAuth.enabled` defaults to `false`. With `agent_key`, you must configure worker identity. The\nvalues in [step 3](#3-create-ram-valuesyaml) do this.\n\nIn version 0.7.0, the Data Plane fetches the JWKS without credentials and trusts only the\ncertificate authority (CA) bundle in its image. What you set up depends on where the cluster's\nissuer is served:\n\n| Cluster | `issuer` | `jwks_uri` | Extra steps | Tested |\n|---------|----------|------------|-------------|--------|\n| Self-managed (kind, kubeadm, on-premises) | from `kubectl get --raw /.well-known/openid-configuration` (kind: `https://kubernetes.default.svc.cluster.local`) | `https://kubernetes.default.svc/openid/v1/jwks` | `cluster-ca` Secret + `tls.caCertSecret: cluster-ca` + ClusterRoleBinding (cluster admin's decision) | Yes, on kind |\n| Amazon Elastic Kubernetes Service (EKS) | `aws eks describe-cluster --name <cluster> --query cluster.identity.oidc.issuer --output text` | the `jwks_uri` field of `<issuer>/.well-known/openid-configuration` | Data Plane egress to the issuer host, or the `com.amazonaws.<region>.oidc-eks` PrivateLink endpoint with private DNS. The hostname differs for IPv6 clusters and AWS China, so copy it, don't type it | No |\n| Azure Kubernetes Service (AKS), OpenID Connect (OIDC) issuer enabled | `az aks show -n <cluster> -g <resource-group> --query oidcIssuerProfile.issuerUrl -o tsv` (exact, including the trailing slash) | the `jwks_uri` field of `<issuer>.well-known/openid-configuration` | Data Plane egress. The issuer is on by default only for new Standard clusters on 1.34+ and on AKS Automatic. `--enable-oidc-issuer` on an existing cluster restarts the API server and cannot be undone | No |\n| Google Kubernetes Engine (GKE) | as self-managed | as self-managed | as self-managed, because anonymous access to the GKE `jwks` endpoint is not verified | No |\n| Air-gapped (any) | as self-managed | as self-managed | as self-managed | Yes, on kind |\n\nAlways take `jwks_uri` from the discovery document. Don't build it from the issuer, because the AKS\npath differs. On EKS and AKS, replace `issuer` and `jwks_uri` in the values, remove `tls`, and skip\nthe rest of this section.\n\nOn self-managed, GKE, and air-gapped clusters, create a Secret from the cluster CA so the Data\nPlane trusts the API server certificate:\n\n[code example]\n\nThe Data Plane also needs to read the JWKS without logging in. Allowing that is the cluster\nadmin's decision:\n\n[code example]\n\nThis binding grants only `get` on `/.well-known/openid-configuration` and `/openid/v1/jwks`, which\nserve issuer metadata and public signing keys, not secrets. It applies to unauthenticated callers\nacross the whole cluster. It has no effect where the API server disables anonymous authentication.\nOn those clusters, version 0.7.0 has no working setup for `agent_key` with workers."
    },
    {
      "id": "3-create-ram-valuesyaml",
      "title": "3. Create `ram-values.yaml`",
      "role": "content",
      "text": "[code example]\n\n- `<chat-model>` is any chat model the endpoint serves.\n- `controlplane.configData.embedding.dimensions` must equal `memory.embedding.models.dimensions`.\n- `<service-account-issuer>` is the cluster's issuer. Read it with:\n\n  [code example]\n\n- `tls.caCertSecret` names the CA Secret from [step 2](#prepare-worker-identity-for-your-platform).\n  Remove it on EKS and AKS.\n- The chart writes the agent-key introspection settings and the Control Plane internal token for you.\n- Image tags are not set, so the chart defaults (`0.7.0`) apply.\n\n\nNo external model provider? To try the API with local models, see [Evaluate locally](#evaluate-locally)."
    },
    {
      "id": "before-you-install-check-your-values",
      "title": "Before you install: check your values",
      "role": "content",
      "text": "Check each value in `ram-values.yaml` before you install. If one is wrong, you see the symptom in\nits row. The rows are checklist items CL-1 to CL-6.\n\n| Row | Check | Symptom | Fix |\n| --- | --- | --- | --- |\n| <a id=\"cl-1\"></a>CL-1 | `memory.license.license_path` is set | Server and worker pods `CrashLoopBackOff`; log: `initialize license enforcement: license validation failed; refusing to start: invalid license format` | Set `memory.license.license_path: /etc/redis-agent-memory/license` |\n| <a id=\"cl-2\"></a>CL-2 | `memory.promote_session_memory.strategies.instruct.llm` is set | Worker pods `CrashLoopBackOff` (server stays Ready); log: `promote_session_memory: Strategies: strategies.instruct.llm is required.` | Add the `instruct.llm` block |\n| <a id=\"cl-3\"></a>CL-3 | Each `llm.provider` names an `inference_providers` entry | Server and worker pods `CrashLoopBackOff`; log: `panic: config validation failed: promote_session_memory.strategies.instruct.llm: provider \"<name>\" is not defined in inference_providers (defined: <names>)` | Add `memory.inference_providers.<name>` or fix the name |\n| <a id=\"cl-4\"></a>CL-4 | `memory.dataplane_client.base_url` is `http://redis-agent-memory:9000` | Pods Ready, but no memories appear; worker log: `getting session memory: Get \"http://<wrong-host>:9000/v1/stores/<store-id>/session-memory/...\"` … `dial tcp: lookup <wrong-host>` (the rest depends on the cluster DNS) | Set `http://redis-agent-memory:9000` |\n| <a id=\"cl-5\"></a>CL-5 | No image tag is set, or it exists on Docker Hub | Server and worker pods `ImagePullBackOff`; event: `Failed to pull image \"redislabs/agent-memory:<tag>\": … not found` | Remove `*.image.tag` (defaults `0.7.0`) |\n| <a id=\"cl-6\"></a>CL-6 | `auth.worker_identity` matches the worker (issuer, audience, subject with your namespace) | Pods Ready, but no memories appear; worker log: `memory-dataplane API error: status 401`; the Data Plane logs `401` on `GET /v1/stores/<store-id>/session-memory/<session-id>` with no reason | Fix `issuer` from `/.well-known/openid-configuration` and the namespace in `subject`. The same symptom appears when the Data Plane cannot fetch the cluster JWKS, so also check [worker identity for your platform](#prepare-worker-identity-for-your-platform) |"
    },
    {
      "id": "4-install",
      "title": "4. Install",
      "role": "content",
      "text": "[code example]\n\nOn small clusters, install without `--atomic --wait`, as shown, and watch pod status.\n\nIf you want Helm to wait, set an explicit timeout that matches the environment:\n\n[code example]"
    },
    {
      "id": "5-verify-pods-and-health",
      "title": "5. Verify pods and health",
      "role": "content",
      "text": "[code example]\n\nThe server, worker, `redis-agent-memory-controlplane`, and `redis-agent-memory-identity-service`\npods are Ready.\n\nRun each `port-forward` in this guide in its own terminal. Port-forward the Data Plane and check its\nhealth. The health check doesn't need an agent key:\n\n[code example]\n\nThe response is:\n\n[code example]"
    },
    {
      "id": "6-create-a-store",
      "title": "6. Create a store",
      "role": "content",
      "text": "Use the Control Plane admin token:\n\n[code example]\n\nThe response carries `storeId`."
    },
    {
      "id": "7-mint-an-agent-key",
      "title": "7. Mint an agent key",
      "role": "content",
      "text": "Use the Identity Service control token:\n\n[code example]\n\nUse the same tenant on every grant of a key. Any string is accepted. Use `1` if you manage stores with the Control Plane admin token. The response's `token` is the agent key. It is shown\nonly once."
    },
    {
      "id": "8-make-one-authenticated-call",
      "title": "8. Make one authenticated call",
      "role": "content",
      "text": "[code example]\n\nThe first long-term memories appear up to 5 minutes after the session events, because promotion\nruns at the end of a clock-aligned 300-second window. To shorten the wait, set\n`extractionCadence.activeIntervalSeconds` (60–600) on the store. No API triggers promotion."
    },
    {
      "id": "evaluate-locally",
      "title": "Evaluate locally",
      "role": "content",
      "text": "This setup runs with no external model provider. The `noop` embedder produces meaningless vectors,\nand a small local model produces rough extractions. Use it to try the API, not to judge quality or\nfor production.\n\nSave this Deployment and Service as `ollama.yaml`. Replace `<ollama-version>` with 0.13.3 or later:\n\n[code example]\n\nDeploy it in the release namespace:\n\n[code example]\n\nThe first start downloads about 2 GB.\n\nIn `ram-values.yaml`, replace the provider blocks with:\n\n[code example]\n\n`api_key: ollama` is a placeholder. The client requires a key, and Ollama ignores it.\n\nIn `overlay.yaml`, remove `embedders_connection_details.openai` and both `api_key` entries."
    },
    {
      "id": "next-steps",
      "title": "Next steps",
      "role": "content",
      "text": "- [API examples](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/api-examples) for the full API.\n- [Operations](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations) for key rotation."
    }
  ],
  "examples": [
    {
      "id": "1-create-the-namespace-ex0",
      "language": "bash",
      "code": "kubectl create namespace <namespace-name>",
      "section_id": "1-create-the-namespace"
    },
    {
      "id": "2-create-secrets-ex0",
      "language": "bash",
      "code": "kubectl -n <namespace-name> create secret generic ram-license \\\n  --from-file=license=./ram-license.key\nkubectl -n <namespace-name> create secret generic ram-secrets \\\n  --from-file=overlay.yaml=./overlay.yaml\nkubectl -n <namespace-name> create secret generic ram-ids-metadata \\\n  --from-file=metadata.yaml=./ids-metadata.yaml",
      "section_id": "2-create-secrets"
    },
    {
      "id": "2-create-secrets-ex1",
      "language": "yaml",
      "code": "metadata:\n  urls: [\"redis://<metadata-redis-host>:6379\"]\ndatabases:\n  \"1\":\n    urls: [\"redis://<store-redis-host>:6379\"]\nbackground_jobs:\n  redis:\n    urls: [\"redis://<job-redis-host>:6379\"]\nembedders_connection_details:\n  openai:\n    credentials:\n      api_key: <openai-api-key>\npromote_session_memory:\n  strategies:\n    instruct:\n      llm:\n        credentials:\n          api_key: <openai-api-key>",
      "section_id": "2-create-secrets"
    },
    {
      "id": "2-create-secrets-ex2",
      "language": "yaml",
      "code": "metadata:\n  urls: [\"redis://<metadata-redis-host>:6379\"]",
      "section_id": "2-create-secrets"
    },
    {
      "id": "prepare-worker-identity-for-your-platform-ex0",
      "language": "bash",
      "code": "kubectl -n <namespace-name> get configmap kube-root-ca.crt -o jsonpath=\"{.data.ca\\.crt}\" >./cluster-ca.crt\nkubectl -n <namespace-name> create secret generic cluster-ca --from-file=ca.crt=./cluster-ca.crt",
      "section_id": "prepare-worker-identity-for-your-platform"
    },
    {
      "id": "prepare-worker-identity-for-your-platform-ex1",
      "language": "bash",
      "code": "kubectl create clusterrolebinding redis-agent-memory-oidc-discovery \\\n  --clusterrole=system:service-account-issuer-discovery --group=system:unauthenticated",
      "section_id": "prepare-worker-identity-for-your-platform"
    },
    {
      "id": "3-create-ram-valuesyaml-ex0",
      "language": "yaml",
      "code": "license:\n  existingSecret: ram-license\nconfig:\n  render: true\nsecrets:\n  secretName: ram-secrets\nshared:\n  databases:\n    \"1\":\n      name: default\nworkerAuth:\n  enabled: true\nmemory:\n  license:\n    license_path: /etc/redis-agent-memory/license\n  default_extraction_strategy: instruct\n  embedding:\n    provider: openai\n    models:\n      default_embedding_model: text-embedding-3-small\n      dimensions: 1536\n  embedders_connection_details:\n    openai:\n      base_url: https://api.openai.com\n      credentials:\n        type: static\n  inference_providers:\n    openai:\n      endpoint:\n        base_url: https://api.openai.com/v1\n        timeout: 30s\n        auth_format: bearer\n  promote_session_memory:\n    strategies:\n      instruct:\n        llm:\n          provider: openai\n          credentials:\n            type: static\n          models:\n            default_chat_model: <chat-model>\n  background_jobs:\n    redis:\n      enabled: true\n      queue_prefix: ram\n      worker_regions: [default]\n  request_region:\n    default: default\n  dataplane_client:\n    base_url: http://redis-agent-memory:9000\n    auth:\n      disabled: false\n      type: service_account_token\n      token_file: /var/run/secrets/redis-agent-memory-worker/token\n  auth:\n    worker_identity:\n      enabled: true\n      issuer: <service-account-issuer>\n      jwks_uri: https://kubernetes.default.svc/openid/v1/jwks\n      audience:\n        - redis-agent-memory\n      subjects:\n        - subject: \"system:serviceaccount:<namespace-name>:redis-agent-memory-worker\"\n          user_id: redis-agent-memory-worker\n          roles:\n            - operator\n          resources:\n            \"mem-store:*\":\n              permissions:\n                - write\n  session_summarisation:\n    enabled: false\n  session_summary_view:\n    enabled: false\ncontrolplane:\n  config:\n    render: true\n  configData:\n    profile: prod\n    auth:\n      type: admin-token\n      admin_token:\n        token_file: /etc/controlplane-onprem/admin/token\n    license:\n      license_path: /etc/redis-agent-memory/license\n    embedding:\n      dimensions: 1536\nidentityService:\n  metadata:\n    existingSecret: ram-ids-metadata\ntls:\n  caCertSecret: cluster-ca",
      "section_id": "3-create-ram-valuesyaml"
    },
    {
      "id": "3-create-ram-valuesyaml-ex1",
      "language": "bash",
      "code": "kubectl get --raw /.well-known/openid-configuration | jq -r .issuer",
      "section_id": "3-create-ram-valuesyaml"
    },
    {
      "id": "4-install-ex0",
      "language": "bash",
      "code": "helm repo add redis-ai https://helm.redis.io/ai\nhelm repo update redis-ai\nhelm install redis-agent-memory redis-ai/redis-agent-memory \\\n  --version 0.7.0 \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml\nkubectl -n <namespace-name> get pods -w",
      "section_id": "4-install"
    },
    {
      "id": "4-install-ex1",
      "language": "bash",
      "code": "helm install redis-agent-memory redis-ai/redis-agent-memory \\\n  --version 0.7.0 \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml \\\n  --wait \\\n  --timeout 15m",
      "section_id": "4-install"
    },
    {
      "id": "5-verify-pods-and-health-ex0",
      "language": "bash",
      "code": "kubectl -n <namespace-name> get pods -l app.kubernetes.io/name=redis-agent-memory",
      "section_id": "5-verify-pods-and-health"
    },
    {
      "id": "5-verify-pods-and-health-ex1",
      "language": "bash",
      "code": "kubectl -n <namespace-name> port-forward svc/redis-agent-memory 9000:9000\ncurl http://localhost:9000/health",
      "section_id": "5-verify-pods-and-health"
    },
    {
      "id": "5-verify-pods-and-health-ex2",
      "language": "json",
      "code": "{\"status\":\"healthy\"}",
      "section_id": "5-verify-pods-and-health"
    },
    {
      "id": "6-create-a-store-ex0",
      "language": "bash",
      "code": "kubectl -n <namespace-name> port-forward svc/redis-agent-memory-controlplane 9100:9100\nRAM_ADMIN_TOKEN=$(kubectl -n <namespace-name> get secret \\\n  redis-agent-memory-controlplane-admin-token -o jsonpath='{.data.token}' | base64 -d)\ncurl -sS -X POST http://localhost:9100/v1/stores \\\n  -H \"Authorization: Bearer $RAM_ADMIN_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"my-store\"}'",
      "section_id": "6-create-a-store"
    },
    {
      "id": "7-mint-an-agent-key-ex0",
      "language": "bash",
      "code": "kubectl -n <namespace-name> port-forward svc/redis-agent-memory-identity-service 9200:9200\nIDS_CONTROL_TOKEN=$(kubectl -n <namespace-name> get secret \\\n  redis-agent-memory-identity-service-control-token -o jsonpath='{.data.token}' | base64 -d)\ncurl -sS -X POST http://localhost:9200/v1/api-keys \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-agent-key\",\n    \"grants\": [\n      {\n        \"tenant\": \"<your-tenant-id>\",\n        \"product\": \"memory\",\n        \"resourceType\": \"mem-store\",\n        \"resourceId\": \"<store-id>\",\n        \"actions\": [\"read\", \"write\"]\n      }\n    ]\n  }'",
      "section_id": "7-mint-an-agent-key"
    },
    {
      "id": "8-make-one-authenticated-call-ex0",
      "language": "bash",
      "code": "curl -sS -X POST \"http://localhost:9000/v1/stores/<store-id>/session-memory/events\" \\\n  -H \"Authorization: Bearer <agent-key>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"sessionId\": \"session-001\",\n    \"actorId\": \"user-001\",\n    \"role\": \"USER\",\n    \"content\": [{\"text\": \"What is the capital of France?\"}],\n    \"createdAt\": \"2026-06-25T18:00:00Z\"\n  }'",
      "section_id": "8-make-one-authenticated-call"
    },
    {
      "id": "evaluate-locally-ex0",
      "language": "yaml",
      "code": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: ollama\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: ollama\n  template:\n    metadata:\n      labels:\n        app: ollama\n    spec:\n      containers:\n        - name: ollama\n          image: ollama/ollama:<ollama-version>   # 0.13.3 or later\n          env:\n            - name: OLLAMA_CONTEXT_LENGTH\n              value: \"8192\"\n          command: [\"/bin/sh\", \"-c\"]\n          args: [\"ollama serve & until ollama list >/dev/null 2>&1; do sleep 1; done; ollama pull qwen2.5:3b; wait\"]\n          ports:\n            - containerPort: 11434\n          resources:\n            requests:\n              cpu: \"2\"\n              memory: 4Gi\n          volumeMounts:\n            - name: models\n              mountPath: /root/.ollama\n      volumes:\n        - name: models\n          emptyDir: {}\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: ollama\nspec:\n  selector:\n    app: ollama\n  ports:\n    - port: 11434\n      targetPort: 11434",
      "section_id": "evaluate-locally"
    },
    {
      "id": "evaluate-locally-ex1",
      "language": "bash",
      "code": "kubectl -n <namespace-name> apply -f ollama.yaml",
      "section_id": "evaluate-locally"
    },
    {
      "id": "evaluate-locally-ex2",
      "language": "yaml",
      "code": "memory:\n  embedding:\n    provider: noop\n    models:\n      default_embedding_model: noop\n      dimensions: 384\n  embedders_connection_details:\n    noop:\n      protocol: noop\n  inference_providers:\n    ollama:\n      protocol: openai\n      endpoint:\n        base_url: http://ollama:11434/v1\n        timeout: 300s\n      http_client:\n        timeout: 300s\n  promote_session_memory:\n    strategies:\n      instruct:\n        llm:\n          provider: ollama\n          credentials:\n            type: static\n            api_key: ollama\n          models:\n            default_chat_model: qwen2.5:3b\ncontrolplane:\n  configData:\n    embedding:\n      dimensions: 384",
      "section_id": "evaluate-locally"
    }
  ]
}
