-
Notifications
You must be signed in to change notification settings - Fork 5
Sizing And Hardware
Capacity planning for jina-on-prem deployments. Covers GPU vs CPU choice, VRAM by model, expected throughput, disk, and redundancy.
flowchart LR
A[Pick a model] --> B[Pick a runtime]
B --> C[Size the host]
C --> D[Estimate throughput]
D -.- E[If under, add replicas
or move to bigger GPU]
CPU image (:cpu tag) |
GPU image (:gpu tag) |
|
|---|---|---|
| Base | python:3.11-slim |
pytorch/pytorch:2.5.1-cuda12.1-cudnn9-devel |
| Image size | 2-4 GB | 8-12 GB |
| Needs GPU on host | no | yes (CUDA 12.1+, driver 525+) |
| Latency, single request | 200-2000 ms | 10-200 ms |
| Throughput, batched | ~5-20 docs/s | 100-1000 docs/s |
| Cost on GCP (1 month, on-demand) | ~$50 (e2-standard-4) | ~$500 (g2-standard-4 + L4) |
Rule of thumb: < 10 QPS sustained -> CPU is fine and a lot cheaper. > 10 QPS or latency-sensitive interactive use -> GPU.
| Model | Min VRAM | Recommended GPU | Notes |
|---|---|---|---|
| jina-embeddings-v5-text-nano | 2 GB | T4 / L4 | CPU also fine |
| jina-embeddings-v5-text-small | 3 GB | L4 / A10G | T4 OK with smaller batches |
| jina-embeddings-v5-omni-small | 8 GB | L4 / A10G / A100 | multimodal |
| jina-embeddings-v4 | 10 GB | A10G / A100 | multimodal, 32K |
| jina-reranker-v3 | 3 GB | L4 | 131K context |
| jina-clip-v2 | 4 GB | L4 | text + image |
| jina-code-embeddings-1.5b | 4 GB | L4 | |
| ReaderLM-v2 | 4 GB | L4 | 512K context, plan for bigger batches |
Full per-model VRAM in the Model Catalog.
L4 is the workhorse. 24GB, sane price, available in most regions, handles any model except v4-multimodal. Default to it unless the customer needs A100 latency or has L4 stockout (see Troubleshooting -> L4 stockout).
flowchart LR
A[Connected builder
30-200 GB] --> B[.tar.gz
2-12 GB per model]
B --> C[Air-gapped host
20-100 GB depending on
how many models]
- Builder machine: 100 GB minimum if bundling 1-2 models. 200 GB+ if bundling all 6 priority models. Each build holds: built image (2-12 GB) + tar.gz (1-7 GB) + Docker BuildKit cache (up to 20 GB).
- Air-gapped host: needs ~2x the image size at runtime (image + Docker layer cache). For a 4 GB image, plan 10 GB disk.
-
Reclaim:
docker builder prune -afbetween bundles.docker system prune -fafter.
Rough throughput on a single L4, batch size 32:
| Model | Tokens/s | Documents/s (avg 50 tokens) |
|---|---|---|
| v5-text-nano | ~30,000 | ~600 |
| v5-text-small | ~12,000 | ~240 |
| v5-omni-small (text only) | ~6,000 | ~120 |
| reranker-v3 | ~150 doc-query pairs/s |
(Measure on the customer hardware. These are starting estimates from scripts/benchmark.py.)
For higher throughput:
- Batch at the client. Send 32-256 inputs per request, not one at a time.
- Use matryoshka to truncate output dims at query time if the index dim doesn't need to be full size.
- Multiple replicas. A 4-vCPU + 1xL4 host can run one container. Two hosts -> 2x. Load balance with anything (nginx, HAProxy, ALB).
- Bigger GPU. A10G is ~2x L4, A100 ~4x for these models.
Where the jina-on-prem container sits in a typical customer architecture:
outside │ customer perimeter (firewall / VPN / ZTNA)
─────────┼────────────────────────────────────────────────────
│
user ──┼──► ingress ──► app ──► jina-on-prem ─► ╳ internet
│ │ :8080 (blocked)
│ │
│ └──► Elasticsearch
│ └ (optional inference call)
│ back into jina-on-prem
Three common placements:
- Sidecar to the app: jina-on-prem and the calling app on the same VM/pod. Lowest latency (localhost). Best when the app needs many embedding calls per request.
- Shared service: one jina-on-prem host serving multiple apps via internal DNS. Easier to right-size; one place to patch.
- Behind Elasticsearch: ES inference service calls jina-on-prem at indexing and query time. Apps talk only to ES. Cleanest for search-only stacks.
Three patterns:
single host active-active k8s
────────── ───────────── ───
┌──► pod A
client ─► jina client ─► LB ─► host A │
│ host B LB ──► pod B
└──► host B │
└──► pod N
- Single host is fine for POC, dev, internal tools. No redundancy.
- Active-active with two hosts behind any L4 load balancer. Models are stateless so any request can go to any replica. Use this for production. Maintain spare image tarballs on disk so you can rebuild a host without rebundling.
-
Kubernetes if the customer is already running it. Each pod is
docker runwith aServiceandDeployment. Persistent volume not needed (model is in the image). NodeSelector for GPU nodes if you mix GPU and CPU.
A ready-to-apply manifest with Namespace + Deployment + Service + HPA + Ingress lives at k8s/jina-on-prem.yaml:
# Load the image on every node (no internal registry needed):
for n in node-1 node-2 node-3; do
docker save jina/jina-embeddings-v5-text-small:gpu | ssh $n "docker load"
done
kubectl apply -f k8s/jina-on-prem.yaml
kubectl -n jina-on-prem rollout status deployment/jina-embed
kubectl -n jina-on-prem port-forward svc/jina-embed 8080:8080
curl http://localhost:8080/healthThe manifest includes:
-
Deploymentwith 2 replicas, rolling update strategy, GPUnodeSelector -
Service(ClusterIP) on port 8080 -
HorizontalPodAutoscalerthat scales 2-8 pods based on CPU -
Ingressexample for nginx-ingress (comment out if not using) - Readiness probe (30s initial, 60s grace) + liveness probe (90s initial)
To run two models (embed + rerank), copy the Deployment+Service blocks with different names and images.
Before you go on-site or hand over a bundle:
- Host with Docker 24+ installed
- (GPU) NVIDIA driver >= 525, CUDA Container Toolkit installed (
nvidia-smiworks in container) - Disk: 2-3x the bundle size free
- Port 8080 (or chosen port) free
- Network plan: how does the calling app reach this host? localhost / LAN IP / internal DNS?
- (Optional) Load balancer in front if multi-replica
If the customer has none of this set up, scripts/bootstrap-gcp.sh is a template - copy it and adapt for their cloud or on-prem.
- Bundling Guide - how to actually build the .tar.gz
- Quick Start - first-deploy walkthrough
- Troubleshooting - VRAM OOM, CUDA mismatch, etc
Repo - Issues - Releases - Commercial license
Documentation source lives in docs/; edit there via PR and run ./scripts/sync-wiki.sh to mirror to the wiki.