Storage
Local disk configuration for Berserk services — caching, temporary storage, and persistence options
Berserk services use local disk for caching and temporary storage. The source of truth for all segment data is S3 (configured in Dependencies), so local storage is ephemeral — losing it means a cold cache, not data loss.
Query Service
The query service uses a local segment cache to avoid re-fetching segments from S3 on every query. For best performance, use a local SSD or NVMe-backed disk. The cache mount point is /segment_cache.
The cache is partitioned across query nodes rather than replicated — each segment has one owning node that caches and scans it. See Segment Placement for how ownership is decided and what happens when you add or remove a node.
Size the cache for the days of data you want available for fast searches: ingest per day / compression x cache days, with compression around 10x depending on your data. One TB/day of ingest with a 7-day cache is about 700 GB. With multiple query nodes, each node holds its own share of that total. The Hardware Sizing page has the full model and a calculator.
Uses ephemeral node storage. Simple to set up but the cache is lost when the pod is rescheduled.
query:
cache:
enabled: true
persistent: false
size: "128Gi"Mounts a directory from the host node — ideal when you have a local NVMe disk mounted at a known path. The cache survives pod restarts on the same node.
query:
cache:
enabled: true
persistent: false
hostPath: "/mnt/nvme"Uses a PersistentVolumeClaim. The Helm chart deploys query as a StatefulSet in this mode so the PVC is retained across restarts. Use a storageClass backed by local NVMe or fast SSD for best results.
query:
cache:
enabled: true
persistent: true
size: "128Gi"
storageClass: "local-nvme" # your storage classFor production workloads, a local NVMe-backed disk (via hostPath or emptyDir) gives the best query latency. The cache is purely a performance optimization — S3 remains the source of truth.
Other Services
The janitor, nursery, and ingest services use local disk for working storage (e.g., segment merging scratch space, baby segment buffering). Disk speed is not critical for these services — standard node storage is sufficient.
All three use emptyDir volumes — no storage classes or dynamic provisioning required. The nursery's working dir also holds its disk-resident node identity, and emptyDir gives it the durability that matters: the volume belongs to the pod, so a crashed container restarts with identity and cache intact. Pod replacement (a deploy, an eviction, a node failure) is a disk loss by design — the nursery mints a fresh identity and its stream groups fail over automatically (~30 seconds), then it rebuilds the small baby-segment cache from object storage.
janitor:
cache:
size: "30Gi" # merge scratch: at least 6x the meta max_segment_size (5 GB default); a hard cap
nursery:
workingDir:
size: "50Gi" # baby segment buffer + node identity (emptyDir size limit)
# ingest uses a small emptyDir with no size limit by defaultWith a local-path class the PVC pins the nursery pod to the node holding the volume. Losing that node (or the volume) is handled by design: the nursery mints a fresh identity on the empty disk and its stream groups fail over automatically — no manual recovery step.
Node Scheduling
Control which Kubernetes nodes Berserk services run on using nodeSelector, tolerations, and affinity. Each can be set globally or per-service — per-service values take precedence.
Global defaults
Apply scheduling constraints to all services:
global:
nodeSelector:
disktype: ssd
tolerations:
- key: "dedicated"
operator: "Equal"
value: "berserk"
effect: "NoSchedule"
affinity: {}Per-service overrides
Override global defaults for individual services:
query:
nodeSelector:
kubernetes.io/hostname: my-dedicated-nodePinning to a specific node
To pin a service to a specific node by hostname:
query:
nodeSelector:
kubernetes.io/hostname: my-node-name
nursery:
nodeSelector:
kubernetes.io/hostname: my-node-name