> For the complete documentation index, see [llms.txt](https://docs.envector.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.envector.io/1.4.x/get-started/installation/helm-chart.md).

# Helm Chart

### Overview

This guide explains how to deploy **enVector services on an existing Kubernetes cluster** using Helm. It focuses on configuring external dependencies (database, storage) and license management, plus options to manage sensitive values safely.

Running enVector on Kubernetes provides:

* **Parity**: Closer to production-grade behavior than Docker Compose
* **Scalability**: Horizontal Pod Autoscaling (HPA) and node pools
* **Security**: Namespaces, RBAC, and network policies
* **Extensibility**: Ability to integrate with Ingress controllers, TLS (e.g., cert-manager), storage classes, and monitoring stacks (to be installed separately)

### Architecture

The chart deploys the following enVector microservices:

* `envector-endpoint` — public API gateway and client interface (gRPC + HTTP health/admin).
* `envector-backend` — service orchestration and metadata management.
* `envector-orchestrator` — schedules compute requests across compute workers.
* `envector-compute` — executes encrypted vector search; mounts the license token.
* `envector-shaper` — handles split/merge of row shards against object storage.

### Prerequisites

Prepare the following before deploying:

* **Kubernetes cluster** running (local or managed, e.g. GKE, OKE, EKS)
* **kubectl v1.28+** and **Helm v3.12+** installed
* **Administrative privileges** on the cluster
* **32GB+ RAM** and **50GB+ free disk space** available in the cluster
* **Docker Hub access token** (for pulling private enVector images)
* **PostgreSQL service available** (e.g., OCI Database with PostgreSQL, GCP Cloud SQL, or equivalent)
* **S3-compatible object storage service available** (e.g., OCI Object Storage, AWS S3, MinIO, etc.)
* (Optional) **Ingress, TLS, Monitoring, StorageClass, NetworkPolicy** — not included in the Helm chart; install separately if required

### Step 1: Get the Helm Chart from GitHub

```bash
mkdir helm_chart

# Clone the repository
git clone https://github.com/CryptoLabInc/envector-deployment.git

# Copy Helm chart directory
cp -r envector-deployment/kubernetes-manifests/helm ./helm_chart
cd helm_chart
```

#### Release naming & ServiceAccount (IRSA / Workload Identity)

Before editing `values.yaml`, decide how the release name and ServiceAccount should look:

* `nameOverride` / `fullnameOverride` let you pin predictable resource names if your platform enforces naming rules.
* `labels` adds custom labels to every Kubernetes object (helpful for cost allocation or policy engines).
* `serviceAccount.create`, `serviceAccount.name`, and `serviceAccount.annotations` control the ServiceAccount shared by all Pods. Add IAM, IRSA, or Workload Identity annotations here instead of editing templates manually.

These knobs live near the top of `values.yaml` so you can align the deployment with your organization’s naming and IAM standards before moving on to the rest of the configuration.

### Step 2: Configure values.yaml (DB, Storage, License)

The chart ships with a `values.yaml`. Update these keys to match your environment.

#### 2.1 Metadata DB (PostgreSQL)

```yaml
externalServices:
  metadatadb:
    # libpq DSN format
    url: "user=<user> password=<password> host=<host> port=<port> dbname=<database> sslmode=require options='-c search_path=public'"
    schema: "public"   # default schema
```

Notes:

* Use a PostgreSQL endpoint reachable from the cluster.
* Adjust `sslmode` to your security posture (require, verify-ca, verify-full, disable).
* `schema` is passed as `ENVECTOR_DB_SCHEMA`.

#### 2.2 Object Storage (S3-compatible, e.g., MinIO/S3/OCI)

```yaml
externalServices:
  storage:
    # provider: s3 (default, S3-compatible) or gcs (GCP native via workload identity)
    provider: s3
    host: <storage_host>             # e.g., minio.minio.svc, s3.amazonaws.com, <gateway>
    port: <storage_port>             # e.g., 9000 for MinIO, 443 for AWS S3
    user: <access_key>
    password: <secret_key>
    secure: true                     # true=https, false=http
    region: <region>                 # e.g., us-east-1, ap-seoul-1
    create: false                    # true=create bucket on startup
    bucket_name: <bucket_name>
    hasHealthcheckEndpoint: false    # true only for MinIO; false for AWS/OCI/etc.
```

Notes:

* `envector-backend` waits for the storage endpoint (`host:port`) before starting.
* For AWS S3, set `secure: true`, `port: 443`, and `hasHealthcheckEndpoint: false`.
* For MinIO, set the MinIO service DNS/port and you may set `hasHealthcheckEndpoint: true`.
* When `provider: gcs`, `host`, `port`, `user`, `password`, `secure`, and `region` are not used — GCS auth uses the pod's service account via Workload Identity.

#### 2.3 License (compute)

The `envector-compute` component reads a license token from `/envector/license/token.jwt`. You have three ways to provide it:

1. Chart-managed Secret (simple & secure by default)

```yaml
compute:
  license:
    enabled: true
    createSecret: true
    tokenPath: "/envector/license/token.jwt"
    token: ""          # leave empty in values; provide at install time via --set-file
```

First install (creates the Secret using your token file):

```bash
helm install envector . \
  --set compute.license.enabled=true \
  --set compute.license.createSecret=true \
  --set-file compute.license.token=./token.jwt
```

Upgrades reuse the existing Secret automatically. To rotate the token:

```bash
helm upgrade envector . --set-file compute.license.token=./new-token.jwt
```

2. Use an existing Secret you manage

```yaml
compute:
  license:
    enabled: true
    createSecret: false
    existingSecret: my-license   # your Secret name
    secret:
      key: token.jwt             # key inside the Secret
```

Or via CLI:

```bash
helm upgrade --install envector . \
  --set compute.license.enabled=true \
  --set compute.license.createSecret=false \
  --set compute.license.existingSecret=my-license
```

3. External Secrets Operator (ESO) – recommended for centralized secret mgmt

See 2.4 below to enable ESO and map the token.

#### 2.4 Managing sensitive values via External Secrets Operator (optional)

Set `externalSecrets.<name>.enabled=true` for each credential type you want ESO to manage. The blocks share a global `secretStoreRef` but can each override it:

```yaml
externalSecrets:
  secretStoreRef:
    kind: ClusterSecretStore
    name: my-secret-store
  dbSecret:
    enabled: true
    data:
      - secretKey: ENVECTOR_DB_URL
        remoteRef:
          key: prod/app/db
          property: db_url
  storageSecret:
    enabled: true
    data:
      - secretKey: ENVECTOR_STORAGE_USER
        remoteRef: { key: prod/minio, property: accessKey }
      - secretKey: ENVECTOR_STORAGE_PASSWORD
        remoteRef: { key: prod/minio, property: secretKey }
  license:
    enabled: true
    remoteRef:
      key: prod/envector/license
      property: token.jwt
```

Notes:

* `secretStoreRef` is mandatory whenever any ESO block is enabled.
* `dbSecret.data` and `storageSecret.data` mirror the env keys that Pods expect (`ENVECTOR_DB_URL`, `ENVECTOR_STORAGE_USER`, `ENVECTOR_STORAGE_PASSWORD`).
* The license ExternalSecret only renders when both `externalSecrets.license.enabled=true` **and** `externalSecrets.license.remoteRef.key` are set.
* Need to mix AWS Parameter Store (for DB/storage) with Secrets Manager (for license)? Set `externalSecrets.<name>.secretStoreRef` under each block to point at different SecretStores; if omitted, each block falls back to the global `secretStoreRef`.
* Non-sensitive values such as storage `host`, `port`, `secure`, `region`, `bucket_name`, and DB `schema` still live under `externalServices`.

#### 2.5 Image pull secret

Create image pull secret (example name: `regcred`):

```bash
kubectl create secret docker-registry regcred \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username="<YOUR_DOCKERHUB_USERNAME>" \
  --docker-password="<YOUR_DOCKERHUB_ACCESS_TOKEN>" \
  --docker-email="<YOUR_EMAIL>"
```

Required: set this to the Docker registry Secret you created with your Docker token. There is no default; configure it explicitly. Example using the Secret name `regcred`:

```yaml
imagePullSecrets: [regcred]
```

#### 2.6 Image tags (version alignment)

Set the image tags in `values.yaml` to match the enVector version you plan to deploy. Avoid using `latest` in production. Example (v1.4.0):

```yaml
endpoint:
  image:
    tag: v1.4.0
backend:
  image:
    tag: v1.4.0
orchestrator:
  image:
    tag: v1.4.0
shaper:
  image:
    tag: v1.4.0
compute:
  # cpu or gpu; when set to gpu, the chart appends "-gpu" to the tag automatically
  variant: cpu
  image:
    tag: v1.4.0
```

#### 2.7 (Optional) Embedded PostgreSQL / MinIO for evaluation

If you don't have an external database or object store yet, the chart can deploy a single-replica PostgreSQL and/or MinIO StatefulSet for evaluation only (not for production):

```yaml
embeddedPostgres:
  enabled: true
  user: envector
  password: <set-a-strong-password>
  database: envector
  storage:
    size: 1Gi

embeddedMinio:
  enabled: true
  rootUser: minioadmin
  rootPassword: <set-a-strong-password>
  storage:
    size: 20Gi
```

When embedded mode is enabled, the chart wires the in-cluster service into `externalServices.metadatadb` / `externalServices.storage` automatically.

### Step 3: Dry-run (render & validate without installing)

```bash
helm upgrade --install envector . --dry-run --debug
```

Check for:

* No template errors
* Expected env vars including DB/Storage config
* Image pull secret reference present (e.g., `regcred`)

### Step 4: Install the chart

```bash
helm upgrade --install envector .
```

Verify resources:

```bash
kubectl get pods,svc
```

### TLS/HTTPS (Ingress)

Optional: TLS/HTTPS is not required. The chart works over plain HTTP by default. Enable one of the options below only if you want HTTPS via Ingress, or terminate TLS at an external load balancer instead.

#### Prerequisites

* Install an Ingress Controller (e.g., NGINX) and note the `ingress.className`.
* Point your domain (DNS) to the Ingress Service `EXTERNAL-IP`.
* Optional: Install cert-manager and create a ClusterIssuer (recommended).

#### Quick Setup: cert-manager + ClusterIssuer (if not installed)

```bash
# Install cert-manager with CRDs
helm repo add jetstack https://charts.jetstack.io && helm repo update
helm install cert-manager jetstack/cert-manager -n cert-manager --create-namespace --set installCRDs=true

# Create a staging ClusterIssuer (validate here first)
cat > letsencrypt-staging.yaml << 'EOF'
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    email: you@example.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-staging
    solvers:
    - http01:
        ingress:
          class: nginx
EOF
kubectl apply -f letsencrypt-staging.yaml

# Verify
kubectl get clusterissuer

# After validation, create a production issuer similarly
# server: https://acme-v02.api.letsencrypt.org/directory
```

#### Option A) cert-manager automated issuance (recommended)

Enable Ingress in `values.yaml` and set the ClusterIssuer.

```yaml
ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  hosts:
    - host: app.example.com
      paths:
        - path: /
          pathType: Prefix
          service:
            name: endpoint
            port: 50050
  tls:
    - secretName: app-tls
      hosts:
        - app.example.com
```

Deploy/upgrade (namespace optional):

```bash
helm upgrade --install envector . -n <namespace> --create-namespace

# Note: when using --set, escape dots in annotation keys
helm upgrade --install envector . -n <namespace> \
  --set ingress.enabled=true \
  --set ingress.className=nginx \
  --set ingress.annotations."cert-manager\.io/cluster-issuer"=letsencrypt-prod \
  --set ingress.tls[0].secretName=app-tls \
  --set ingress.tls[0].hosts[0]=app.example.com \
  --set ingress.hosts[0].host=app.example.com
```

#### Option B) Manual TLS Secret (private CA or internal networks)

Create a TLS Secret from your PEM files, then reference it in `values.yaml`.

```bash
kubectl create secret tls app-tls \
  --cert=fullchain.pem --key=privkey.pem -n <namespace>
```

```yaml
ingress:
  enabled: true
  className: nginx
  hosts:
    - host: app.example.com
      paths:
        - path: /
          pathType: Prefix
          service:
            name: endpoint
            port: 50050
  tls:
    - secretName: app-tls
      hosts:
        - app.example.com
```

#### Testing

```bash
# DNS
nslookup app.example.com

# Pre-propagation HTTPS check
curl --resolve app.example.com:443:<LB_IP> https://app.example.com -vk

# Ingress status
kubectl get ingress -n <namespace>

# HTTPS response
curl -I https://app.example.com

# Certificate details
openssl s_client -connect app.example.com:443 -servername app.example.com -showcerts

# With cert-manager
kubectl get certificate,challenge,order -n <namespace>
kubectl logs -n cert-manager deploy/cert-manager
```

#### Troubleshooting

* Port 80 blocked or pre-redirected: HTTP-01 fails → allow 80 or use DNS-01.
* Ingress class mismatch: `ingress.className` must match your controller.
* Hostname mismatch: `spec.rules.host` must match TLS hosts.
* DNS propagation delay: wait for new records to propagate.
* Secret format: `kubernetes.io/tls` with `tls.crt` / `tls.key`.
* Private cluster: public ACME unreachable → issue via DNS-01.
* Node clock skew: ACME errors → sync nodes with NTP.

### Ingress health checks

If your Ingress or external load balancer requires an HTTP health‑check path (e.g., AWS ALB Target Group), the services expose lightweight health endpoints on the endpoint's HTTP health port (default `8080`):

* GET `/health`: liveness probe
* GET `/health/ready`: readiness probe (200 OK when backend is healthy, 503 otherwise)
* GET `/admin/services`, `/admin/indexes`, `/admin/keys`: Admin API (when `endpoint.admin.enabled=true`)
* GET `/swagger/`: Swagger UI

Example (AWS Load Balancer Controller with ALB): set annotations and route `/health` to the endpoint service exposed via Ingress.

```yaml
ingress:
  enabled: true
  className: alb
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/healthcheck-path: /health
    alb.ingress.kubernetes.io/healthcheck-interval-seconds: "15"
    alb.ingress.kubernetes.io/success-codes: "200"
  hosts:
    - host: envector.example.com
      paths:
        - path: /
          pathType: Prefix
          service:
            name: endpoint
            port: 50050
        - path: /health
          pathType: Exact
          service:
            name: endpoint
            port: 50050
```

Notes:

* If you terminate TLS at the Ingress, keep the health‑check path under the same host and scheme.
* For NGINX Ingress, backend Pod readiness typically controls endpoint health; you can still expose `/health` if your upstream L7 health checker needs a path.

### Notes & tips

* License: `envector-compute` (and optionally `envector-shaper`) mounts the license at `/envector/license` and reads the `ENVECTOR_LICENSE_TOKEN` path from ConfigMap. Keep the Secret key name aligned with `compute.license.secret.key` (default: `token.jwt`).
* Storage: for MinIO, you may enable `hasHealthcheckEndpoint: true`. For AWS/OCI, keep it `false`.
* Networking: ensure Pods can reach DB and storage endpoints (Security Groups/NetworkPolicies/Firewalls).
* Upgrades: when using an external Secret for the license, you can force a rollout by bumping `compute.license.secret.checksum` (e.g., `sha256sum token.jwt | cut -d' ' -f1`).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.envector.io/1.4.x/get-started/installation/helm-chart.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
