Skip to main content
You grant Mixpeek a namespace. Mixpeek deploys the whole stack into it: the API and services, MVS, Redis, and a RayService running our own Ray, image and CUDA userspace. Batch runs as ephemeral RayJobs in the same namespace. This page is the list of what has to be true of your cluster first. The qualification suite checks every item on it and gives you the report before anything is installed.

Cluster

  • KubeRay already installed, either cluster-wide or watching the namespace you grant. Mixpeek does not install it and does not need to own it. This is the one cluster-scoped prerequisite, and Security and RBAC covers why and for how long.
  • KubeRay 1.1 or newer. Hard requirement. Below it submissionMode: HTTPMode does not exist and batch cannot be submitted at all.
  • Kubernetes 1.33 or newer is optional. From 1.33 Ray’s autoscaler can use in-place vertical scaling (pods/resize), which the kit’s autoscaler Role grants. On older clusters that rule is inert and Ray replaces pods to resize them.
  • One namespace, and the two Roles from Security and RBAC.
  • A ResourceQuota you are comfortable with. One ships with the kit. Change the numbers; Mixpeek reads it and sizes batches to fit.

Compute

Node capacity the namespace can schedule onto, with the taints and labels we should target. Tell us the labels and we render them into the deployment rather than assuming yours look like ours. GPU nodes at a known NVIDIA driver baseline for inference and extraction. The suite reports GPU nodes it can see and whether an untolerating pod can actually reach them, which is a different question from whether the nodes exist.

The floor, before a single document

The install brings up thirteen Ray Serve applications and holds them warm. Their replica minimums multiply out to 10.1 CPU sitting idle, and 31.9 CPU if every one of them scales to its ceiling under load. That is the engine alone. It does not count the API, Celery, Redis, or the MongoDB you bring. None of the thirteen requests a GPU. The extractors that use one take it through the batch path, not through a warm Serve replica, so a cluster with no GPU nodes brings the whole plane up healthy and then fails at extraction time rather than at install time. Bring the GPU nodes; the qualification suite will tell you whether an untolerating pod can reach them.

That 10.1 CPU has to fit on one worker

Read the number above as a per-node requirement, not a cluster total. Serve runs its HTTP proxy on the head only, and the head is started with num-cpus: 0 because it holds the cluster’s metadata rather than running work. Every one of the thirteen applications asks for CPU, so none of them can land there. They all go to a worker. Four workers of 3 CPU each add up to 12 and schedule nothing, because no single worker can hold the set. Size the worker group so that one replica has at least 11 CPU and 28 GiB available to Ray, and let autoscaling add replicas above that for batch work. Ray takes its logical CPU count from the container’s limit, so requests and limits both have to reach that number. Requests above limits is refused by the Kubernetes API server outright, and the pod never appears at all: the RayCluster still reports RayClusterProvisioned: True, because that condition counts the pods that exist. Look at the ReplicaFailure condition when a worker is missing. The applications also ask for a custom Ray resource named serve_node, which the chart declares on both node groups through rayStartParams. If you replace the cluster config with your own, carry that declaration across. Without it every application stays unschedulable, the RayService sits in Initializing with an empty SERVICE STATUS, and nothing reports an error.

Disk, which is larger than the registry suggests

Two images land on every node that runs Mixpeek: the engine image and the platform image that carries the API, Celery and MVS. A registry size is compressed. What a node needs is the unpacked size, and on our own fixture the platform image measured 4.96 GB in the registry and 14.2 GB on disk, a factor of 2.85. The engine image is larger still, around 15.4 GB unpacked. Pulling the platform image took 130 seconds on a well-connected VM. Size nodes for roughly 60 GB of image storage before their own workload, and expect the first pull on a cold node to take minutes rather than seconds.

Egress on first start

An install with no route to the public internet does not come up, and the way it fails is easy to misread as slowness. Three of the thirteen Serve applications fetch their model weights from huggingface.co the first time each replica loads: the reranker (2.2 GB), the SigLIP image encoder (779 MB), and the MiniLM sentence encoder (88 MB). A fourth, the CLAP audio encoder, fetches a checkpoint from the same host the first time it sees audio. Two other models, ArcFace and the multilingual E5 encoder, are baked into the image and need nothing. So you have two options, and we would rather you pick one deliberately:
  • Allow egress to huggingface.co from the Mixpeek namespace, at least on first start. Once a replica has loaded, it does not fetch again.
  • Ask us for a pre-baked image. The engine image already bakes two of the six model sets by exactly this mechanism, so baking the rest is additive. The four together measure 4.9 GB, which is what it would add to the image.
Two applications also call hosted APIs at request time. google__gemini_embedding_2 embeds through Google’s Generative Language and Vertex endpoints, and universal__api, which backs POST /v1/inference, dispatches to OpenAI, Google and Anthropic by model name. Neither is on the path for the extractors and retrievers a standard install uses, so leaving them unreachable does not affect indexing or search. universal__api also accepts a self-hosted OpenAI-compatible endpoint, which keeps POST /v1/inference working with no outbound traffic. Bring the requirement to your deployment conversation if your environment forbids reaching a hosted model provider.

Storage

Two different things, and it is worth separating them because they fail differently. Object storage is where your index lives. MVS keeps durable state in S3 or GCS, not on a disk. Losing a pod does not lose the index; it loses at most the un-replayed write-ahead window, which MVS keeps in a local emptyDir sized per shard. That is a deliberate design: the vector store is object-storage-native, and it is why a node drain is a recovery rather than a restore. A CSI storage class is still required, for the stateful services beside it. Redis runs as a StatefulSet with volumeClaimTemplates and needs a real PersistentVolume. A cluster that cannot provision one cannot run the plane, even though MVS itself would not have noticed.
  • A CSI storage class that provisions persistent volumes surviving pod restarts and rescheduling. The suite writes a nonce from one pod, deletes it, and reads it back from a second pod on the same claim.
  • Throughput and IOPS above the floor for your workload size. The suite measures sequential write and read against a stated floor and fails below it. It does not infer the floor from what it observed, because a bound derived from the measurement can never fail.
  • Object storage, S3 or GCS, reachable from the namespace. This is the one MVS actually depends on, so treat it as the load-bearing requirement rather than the afterthought its position in this list once implied.
On EKS this is not automatic. A stock EKS 1.31 cluster cannot provision a PersistentVolume at all: it ships a gp2 StorageClass that is not marked default, whose provisioner is the in-tree kubernetes.io/aws-ebs that Kubernetes removed in 1.31, and the EBS CSI driver is not among the default addons. It looks like working storage in kubectl get storageclass and provisions nothing. Install the aws-ebs-csi-driver addon and mark a class default before we arrive. GKE hides this by shipping a working default, so an EKS install is where it surfaces.

A MongoDB you provide

The chart ships Redis and does not ship MongoDB. Mixpeek stores its metadata in Mongo: objects, collections, batches, retriever definitions, and the six control-plane collections. The API reads its connection string from the Secret you supply at install. So a database has to exist and be reachable from the namespace before the first install, and it is the one prerequisite on this page that is not a property of your cluster.
  • A MongoDB the namespace can reach, managed or self-run, inside the cluster or outside it. We do not pin a minimum server version: our driver is pymongo 4.9, and what we run and test against is current MongoDB. If you are standing up something older, or a wire-compatible service rather than MongoDB itself, tell us which and we will qualify it rather than guess.
  • A connection string in the install Secret. The API takes its whole environment from a Secret, so the URI goes there rather than into chart values.
  • Sized for metadata, not for media. Documents are records and references; your video stays in object storage. This database stays small relative to the index.
If you intend the fully self-hosted shape, where nothing egresses to Mixpeek, this database is also where the control-plane collections land. See the boundary for what that changes.

Networking and identity

  • Egress from the namespace to your object storage and to the Mixpeek control plane.
  • In-cluster DNS and pod-to-pod routing inside the namespace. A default-deny NetworkPolicy with no allow rule for us is the common cause of a namespace that looks healthy and cannot talk to itself, and the suite separates that from a DNS failure rather than reporting one verdict for both.
  • Workload identity if you want Mixpeek’s pods to reach cloud resources without static credentials. GKE Workload Identity, EKS IRSA and AKS Workload Identity are all recognised.
  • Image pull access to the Mixpeek engine image, either a pull secret in the namespace or a mirror into a registry your nodes already trust.

Which of your customisations we accept

You asked whether we require customisations or direct control of the RayCluster/RayService objects. We own those objects in the granted namespace, and this is the honest list of what your platform’s conventions can change about them. The mechanism is one sentence: the chart renders the rayClusterConfig from values, so anything expressible in a Kubernetes pod template reaches the cluster unmodified. There is no allowlist to extend.
One asymmetry worth knowing before you plan around it. In Mixpeek’s own managed deployments a generator injects scheduling into the pod spec, and there it replaces nodeSelector while it merges tolerations. That path is not the one a customer-hosted install uses, and the chart replaces nothing. We are calling it out because “we accept your node selectors” and “a code path exists that overwrites them” would both be true statements about this repo today, and you should not have to discover which one applies to you.

What turns the chart on, and the one value that must be right

values.yaml is an all-off skeleton. enabled: false for api, celery, mvs, ray, redis and qdrant, each template gated on its own flag, so a bare helm install ./chart renders essentially nothing. What turns components on is an install-values overlay applied with -f. Your deployment ships with an overlay written for your environment, so the values that govern it are the ones in that file. Two consequences worth knowing before your first install. A default written under a disabled component is not a default you get. A storage class, a node selector or a resource request sitting in values.yaml beneath a component that ships off is not what your cluster will do. Your overlay is the file that answers “what will this actually run”, and it is the one to review.
platform is the value that must be right, because the failure is silent. It keeps GKE-specific defaults when set to gke, and anything else turns them off. Left at the GKE default on EKS the install succeeds and the Deployments sit at 0 Ready with healthy containers, because readinessGates names cloud.google.com/load-balancer-neg-ready, which nothing outside Google satisfies. The other three are louder: gcsfuse volume mounts leave pods in ContainerCreating, and the BackendConfig and GMP PodMonitoring API groups do not exist off-GKE, so helm install fails outright. The readiness gate is the dangerous one precisely because it presents as a broken deployment rather than as a wrong value. Your overlay sets it for your distribution.
Portability is not evenly distributed across the chart, and it is not readable from the file names. api.yaml and the four observability templates branch on platform. ray.yaml, mvs.yaml, celery.yaml, redis.yaml and qdrant.yaml carry no such branch. Separately, the generator that emits the Ray cluster spec writes Google node selectors (cloud.google.com/gke-nodepool), so on a non-GKE cluster the node placement in that spec is rendered data we replace for you rather than a value you override.

Your Ray version is not an input

A Ray cluster runs one image and that image is ours. Your Ray, Python and CUDA versions have no bearing on whether Mixpeek works, because we run our own runtime under your scheduler rather than inside yours. That constraint applies only to the shared-cluster integration, which is scoped per cluster.

What stays yours

Node pools, quotas, autoscaler policy, taints and network policy. The qualification suite tests that they stay authoritative: work that must land on your designated pool, work that must be refused for want of a toleration, and work your quota must reject.

Teardown

Removing the kit’s objects removes what Mixpeek created. The suite’s --expect-clean mode runs afterwards and fails if residue remains, so the claim gets checked.