VeltrixDB Docs
v1.1.0 GitHub FAQ Quickstart

Helm Chart

The VeltrixDB Helm chart deploys a production-ready cluster on Kubernetes with per-pod local NVMe storage, Prometheus monitoring, and safe rolling updates — optimized for GKE but portable to any cloud with local NVMe instance types.

Prerequisites

RequirementDetail
Kubernetes1.27+
Helm3.x
Prometheus Operator0.65+ (only required if serviceMonitor.enabled)
GKE node poolsCreated with --local-ssd-interface=NVME
GKE requirement Create node pools with --local-ssd-interface=NVME. Without it, GKE merges all local SSDs into a single RAID-0 device and you lose the parallel I/O benefit.
gcloud container node-pools create veltrixdb-pool \
  --cluster=my-cluster \
  --zone=us-central1-a \
  --machine-type=n2-highmem-64 \
  --num-nodes=3 \
  --local-ssd-count=8 \
  --local-ssd-interface=NVME \
  --disk-size=100GB

Installation

# Add the chart repo
helm repo add veltrixdb https://charts.veltrixdb.io
helm repo update

# Install with defaults
helm install veltrixdb veltrixdb/veltrixdb \
  --namespace veltrixdb \
  --create-namespace

# Install with custom values
helm install veltrixdb veltrixdb/veltrixdb \
  --namespace veltrixdb \
  --create-namespace \
  --values my-values.yaml

Verify the deployment

# Wait for PVs (3 nodes x 8 disks = 24 volumes)
kubectl get pv | grep local-nvme

# Check pods are running
kubectl get pods -n veltrixdb -l app.kubernetes.io/name=veltrixdb

# Test connectivity
kubectl exec -n veltrixdb veltrixdb-0 -- \
  sh -c 'echo PING | nc 127.0.0.1 9000'
# Expected: PONG

What the chart deploys

Confirmed against the templates in VeltrixDB-Helm-Chart/templates/: statefulset.yaml, service.yaml, service-headless.yaml, servicemonitor.yaml, pdb.yaml, prometheusrule.yaml, configmap.yaml, secret.yaml, rbac.yaml, and _helpers.tpl.

NVMe provisioning is not a static chart template Unlike the StatefulSet and PrometheusRule, there is no daemonset.yaml in this chart's templates/ directory. Disk formatting (XFS) and the kubelet mount-namespace workaround for GKE/EKS/AKS are handled by the nvme-prep / mount-keeper DaemonSets that the Kubernetes Operator provisions dynamically per VeltrixCluster, or that you apply directly from VeltrixDB-Kubernetes-Operator/config/manager/nvme-mount-keeper.yaml if running the Helm chart without the operator. Deploy it before helm install — see NVMe Disk Provisioning.

Pod Disruption Budget

The chart's default values.yaml sets podDisruptionBudget.maxUnavailable: 1 — at most one pod may be voluntarily disrupted at a time, which for a 3-node, RF-3 cluster keeps at least 2 nodes available during drains and rolling upgrades.

podDisruptionBudget:
  enabled: true
  maxUnavailable: 1

PrometheusRule alerts

The bundled PrometheusRule (rendered when serviceMonitor.enabled: true) ships 24 alert rules grouped into 10 rule groups, spanning write/read/delete latency, VLog GC health, cluster membership, replication, per-disk health, background scrubbing, and Bloom filter efficiency.

Rule groupAlertsCovers
veltrixdb.write2VeltrixHighWriteLatency (P99 > 50ms), VeltrixWALFlushStalled
veltrixdb.read2VeltrixHighReadLatency (P99 > 5ms), VeltrixLowCacheHitRate (< 50%)
veltrixdb.delete2VeltrixHighDeleteLatency, VeltrixTombstoneBacklogGrowing
veltrixdb.vlog5VeltrixVLogHighGarbageRatio, VeltrixVLogGCStalled, VeltrixVLogGCPermanentlyPaused, VeltrixVLogGCReadErrors, VeltrixVLogFileSizeGrowing
veltrixdb.cluster4VeltrixNodeDown, VeltrixClusterSizeLow, VeltrixFrequentRebalances, VeltrixNodeFailed
veltrixdb.replication2VeltrixHighReplicationLag (> 100ms), VeltrixReplicationFailures
veltrixdb.disk2VeltrixSlowDisk (per-disk latency EWMA ≥ 5x cluster median), VeltrixDiskWriteLatencyHigh (> 20ms)
veltrixdb.scrub2VeltrixCorruptionDetected (CRC32C/magic mismatch), VeltrixScrubReadErrors
veltrixdb.bloom1VeltrixBloomFalsePositiveRateHigh (> 10%, design point ~1%)
veltrixdb.gc2VeltrixGCEmergencyRunsSustained (garbage ≥ 65% for 30 min), VeltrixWriteAdmissionThrottling
Alert count The chart and operator READMEs describe "22 alerts." Counting - alert: entries directly in templates/prometheusrule.yaml gives 24 across the 10 groups above — use the table as the current source of truth.

The most operationally important two: VeltrixCorruptionDetected fires the moment the background scrubber finds a CRC32C or magic mismatch (severity critical, for: 0m) — treat as an immediate SMART-check-and-resync trigger. VeltrixGCEmergencyRunsSustained fires when VLog garbage has sat above the emergency threshold (65%) for 30 minutes and GC is bypassing admission control just to keep up — it means sustained write rate is exceeding GC throughput and you need to reduce load or add disks.

Configuration reference

Key settings from values.yaml. Full defaults are tuned for GKE n2-highmem-96 nodes (480 GB RAM, 96 vCPU, 8 x 375 GiB local NVMe SSDs).

ParameterDefaultDescription
replicaCount3Number of VeltrixDB pods; must match node pool size for correct anti-affinity placement.
image.tagv1.0VeltrixDB container image tag.
storage.disksPerNode8NVMe disks per pod; must match --local-ssd-count on GKE.
storage.diskSizeGi375Capacity per NVMe disk (GKE local SSD = 375 GiB).
storage.storageClassNamelocal-nvmeStorageClass created by the infra provisioner.
storage.numShards1024Index shard count; must be a power of 2 matching the compiled C++ constant.
storage.keyValueSeparationtrueWiscKey-style KV separation — keys in SSTables, values in per-disk VLog.
storage.vlogGCThreshold0.30VLog garbage fraction that triggers defragmentation.
storage.walFlushWindowMs10WAL group-commit window (ms). Must equal vlogFlushWindowMs.
storage.vlogFlushWindowMs10VLog fdatasync group-commit window (ms).
storage.walMaxBatchEntries4096Max WAL entries per group-commit flush before early timer fire.
storage.rawVlog.enabledfalseWrite VLog directly to /dev/nvmeXnY, bypassing XFS. Saves ~25 µs P99 read tail / ~70 µs P99 write tail. Adds SYS_RAWIO capability and a /dev hostPath mount.
storage.rawVlog.devices[]Explicit block-device paths; when empty, inferred as /dev/nvme0n1 ... /dev/nvme(N-1)n1 from disksPerNode.
cache.maxSizeMB262144LIRS cache size in MiB — 256 GiB, sized for 480 GiB nodes (80% rule).
cache.lirRatio0.90Fraction of cache reserved for the high-locality (LIR) resident set.
server.port9000Plain TCP protocol port.
server.tlsPort9443TLS port, active only when tls.enabled.
server.metricsPort2112Prometheus metrics + /healthz + /readyz.
cluster.gossipPort7946UDP gossip failure-detector port.
cluster.replicationFactor3Desired replica count per shard.
resources.requests.memory480GiMemory request per pod.
resources.requests.cpu64CPU request per pod.
resources.requests.hugepages-2Mi2GiHugepages for the C++ allocator and io_uring SQPOLL fixed buffers. Requires vm.nr_hugepages >= 1024 on the host.
autoTune.enabledfalseAuto-detect hardware and self-size cache/shards/compaction threads.
autoTune.sysTunetrueApply sysctl/hugepages/scheduler tuning; requires CAP_SYS_ADMIN or root.
auth.enabledfalseEnable RBAC authentication.
auth.configSecret""Secret name with key auth-config.json.
tls.enabledfalseEnable TLS 1.3 on the server port.
tls.existingSecret""Secret with tls.crt/tls.key.
tls.caSecret""Secret with ca.crt; when set, enables mTLS.
serviceMonitor.enabledtrueCreate the ServiceMonitor + PrometheusRule.
serviceMonitor.interval15sPrometheus scrape interval.
podDisruptionBudget.enabledtrueCreate the PDB.
podDisruptionBudget.maxUnavailable1Max pods voluntarily disrupted at once.
livenessProbe / readinessProbe/healthz @ 30s+10s x3 / /readyz @ 15s+5s x6HTTP probes on metricsPort.
terminationGracePeriodSeconds60Grace period before SIGKILL, giving the WAL time to flush.
affinity.podAntiAffinityrequired, kubernetes.io/hostnameOne VeltrixDB pod per physical host (NVMe disks are node-local).
topologySpreadConstraintsmaxSkew: 1, zoneSpreads pods evenly across zones.

Performance tuning

For 100K+ writes/s, lower the flush windows (both must stay equal):

storage:
  walFlushWindowMs: 5
  vlogFlushWindowMs: 5

For maximum cache hit rate, size the cache to roughly 85% of node RAM:

cache:
  maxSizeMB: 409600  # 400 GB on a 512 GB node

Security, audit, and data-integrity features

TLS and RBAC auth

auth:
  enabled: true
  configSecret: veltrixdb-auth

tls:
  enabled: true
  existingSecret: veltrixdb-tls

At-rest encryption (AES-256-GCM)

kubectl create secret generic veltrixdb-encryption \
  -n veltrixdb \
  --from-literal=key=$(openssl rand 32 | base64)
security:
  encryptionAtRest: true
  encryptionSecretName: veltrixdb-encryption

Each pod loads the key from the VELTRIXDB_ENCRYPTION_KEY env var at startup; without the secret, startup fails when security.encryptionAtRest is true. Key rotation is offline-only.

Background scrubber

scrubber:
  enabled: true
  mbPerSec: 50

CRC32C re-validation of every VLog record at a controlled rate; default 50 MB/s/disk gives roughly a 2-hour full pass on a 375 GB SSD. Mismatches surface via the VeltrixCorruptionDetected alert.

Raw NVMe block-device VLog

For latency-critical clusters, enable storage.rawVlog.enabled to write the VLog directly to block devices instead of XFS files. WAL and segment files continue to live on the XFS PVCs.

storage:
  rawVlog:
    enabled: true
    devices:
      - /dev/nvme0n1
      - /dev/nvme1n1
      - /dev/nvme2n1
      - /dev/nvme3n1
      - /dev/nvme4n1
      - /dev/nvme5n1
      - /dev/nvme6n1
      - /dev/nvme7n1
MetricFile-based VLog (default)Raw block VLog
P99 random read~80 µs~55 µs
P99 4 KB write + sync~250 µs~180 µs
P99.99 spike under sustained writesXFS journal blip every ~30 sFlat
Do not format raw VLog devices The nvme-prep provisioner (or your own) must not run mkfs.xfs on devices destined for raw VLog use. WAL/segment XFS partitions must live on a separate slice of each disk or on the boot disk.

Production values.yaml example

replicaCount: 3

image:
  tag: "1.0.0"

storage:
  disksPerNode: 8
  diskSizeGi: 375

cache:
  maxSizeMB: 262144  # 256 GiB - recommended for 480 GiB nodes

storage:
  walFlushWindowMs: 10
  vlogFlushWindowMs: 10

resources:
  requests:
    memory: 480Gi
    cpu: "60"
  limits:
    hugepages-2Mi: 2Gi

autoTune:
  enabled: false

cluster:
  gossipPort: 7946
  replicationFactor: 3

auth:
  enabled: true
  configSecret: veltrixdb-auth

tls:
  existingSecret: veltrixdb-tls

serviceMonitor:
  enabled: true
  interval: 15s

podDisruptionBudget:
  maxUnavailable: 1

Upgrading

helm upgrade veltrixdb veltrixdb/veltrixdb \
  --namespace veltrixdb \
  --values my-values.yaml

The StatefulSet uses RollingUpdate with maxUnavailable: 1, so pods are replaced one at a time. The PodDisruptionBudget bounds voluntary disruptions from node drains that happen concurrently with the rollout.

Validating a deployment

After install or upgrade, validate the cluster end-to-end with the bench.sh harness shipped in the main VeltrixDB repository. It checks two hard gates: packing density (must stay at or below roughly 152 B/record at 128 B values) and GC emergency runs (must stay at 0 under sustained write stress).

kubectl port-forward -n veltrixdb veltrixdb-0 9000:9000 2112:2112 &

git clone https://github.com/VeltrixDB/veltrixdb.git
cd veltrixdb
ADDR=127.0.0.1:9000 METRICS_ADDR=127.0.0.1:2112 \
NUM_KEYS=10000000 \
BULK_DUR=120 STRESS_DUR=300 \
./scripts/bench.sh

Exit 0 means both gates passed; exit 1 means investigate /tmp/veltrixbench-out/run.log. Re-run after every upgrade that bumps image.tag.

Uninstalling

helm uninstall veltrixdb --namespace veltrixdb

PVCs are not deleted automatically, to protect your data:

kubectl delete pvc -n veltrixdb -l app.kubernetes.io/name=veltrixdb

Troubleshooting

SymptomCause / fix
Pods stuck in PendingCheck PVs are Available; verify the nvme-prep DaemonSet pods are Running; confirm --local-ssd-interface=NVME was set on the node pool.
Write throughput lower than expected on macOS / dev environmentsF_FULLFSYNC costs ~7-10 ms on macOS — a hardware limitation, not a bug. Use Linux NVMe in production.
WAL batch size is 1 (no amortisation)Compare veltrixdb_wal_flushes_total to veltrixdb_writes_total; if roughly equal, raise storage.walFlushWindowMs and storage.vlogFlushWindowMs together.
io_uring buffer registration fails at startupNeeds RLIMIT_MEMLOCK or CAP_IPC_LOCK; add IPC_LOCK to the pod's securityContext.capabilities.add.
VLog not being compactedCheck vlog_gc metrics via curl localhost:2112/metrics inside a pod.