HelixML

Linux & Kubernetes

Install Helix on a Linux server or Kubernetes cluster.

Licensing

A license is required to run Helix on Linux or Kubernetes.

  • Developer license: For individuals, testing, and businesses with less than $10M annual revenue or fewer than 250 employees. Buy a developer license →
  • Enterprise license: For larger organizations with full RBAC, SSO, SOC 2 Type II, and dedicated onboarding. Enterprise licensing →

Helix runs on any Kubernetes cluster - whether that's on your own bare metal, a European cloud provider like Hetzner or OVH, or a major cloud platform like AWS (EKS), Google Cloud (GKE), or Azure (AKS). If you can run containers, you can run Helix.

For organisations focused on digital sovereignty, we also offer a turnkey Sovereign Server - a 4U rack server with Helix pre-installed, shipped to your data centre.

Local Setup

To install Helix on your own Linux server with Nvidia/AMD GPU support, run the following command:

curl -sL -O https://get.helixml.tech/install.sh && bash install.sh

Kubernetes Setup

Helm charts

Helix ships as two separate Helm charts, both published to https://charts.helixml.tech:

ChartPurposeRequired?
helix-controlplaneMain Helix API, UI, RAG, and orchestration. This is what users log in to.Yes
helix-sandboxDocker-in-Docker host for desktop agents, Zed sessions, and spec tasks (cloud desktops with GPU video streaming).Recommended. Required if you use desktop or agent features.

Inference is delivered by external providers (OpenAI, Anthropic, Together, or any OpenAI-compatible endpoint such as a self-hosted vLLM). There is no separate runner chart: configure providers under controlplane.providers.* as shown below.

The sections below cover the install for both charts.

Prerequisites

  • A Kubernetes cluster (1.28+). Managed control planes (AKS/EKS/GKE) are fine; the sandbox chart requires privileged pods, so AKS/EKS/GKE Autopilot is not supported.
  • kubectl and helm 3.x on your workstation.
  • A Helix license key from your account.
  • A StorageClass that supports ReadWriteOnce persistent volumes. The control plane chart provisions several PVCs (main Postgres, pgvector, kodit-vectorchord, controlplane data).
  • For a minimal control-plane-only install, budget ~8 vCPU / 24 GiB RAM of worker capacity. The control plane pod ships with a Haystack sidecar that loads a ~4 GB embedding model on first start.
  • An LLM provider (OpenAI, Anthropic, Together, or any OpenAI-compatible endpoint such as a self-hosted vLLM). Without one, there will be no models available after first login.

Cloud quotas

On a fresh Azure/AWS/GCP subscription, most GPU SKU families have quota 0 by default. Before starting:

  • AWS: g5/p4d EC2 quotas per region.
  • Azure: NVadsV710v5 (AMD V710) or NC*/ND* (NVIDIA) families, plus "Total Regional vCPUs".
  • GCP: nvidia-* GPU quotas per region.

Create secrets

Replace <paste-your-license-key> with the key from your account. Generate real random values for the postgres password, runner token, and encryption key; the example commands below produce them inline.

# Create namespace and secrets
kubectl create namespace helix
kubectl config set-context --current --namespace=helix
 
# Postgres credentials for the bundled Postgres pod (or an external instance).
kubectl create secret generic postgresql-auth-secret \
  --from-literal=username="helix" \
  --from-literal=password="$(openssl rand -hex 16)" \
  --from-literal=database="helix"
 
kubectl create secret generic pgvector-auth-secret \
  --from-literal=username="postgres" \
  --from-literal=password="" \
  --from-literal=database="postgres"
 
kubectl create secret generic helix-pgvector-creds \
  --from-literal=dsn="postgresql://postgres:@my-helix-controlplane-pgvector:5432/postgres"
 
kubectl create secret generic helix-license \
  --from-literal=license-key="<paste-your-license-key>"
 
# Encryption key for secrets at rest (PATs, SSH keys, OAuth tokens).
# Must be a 64-character hex string (32 bytes for AES-256-GCM). DO NOT
# rotate this once data has been written - rotating invalidates every
# stored secret.
kubectl create secret generic helix-encryption-key \
  --from-literal=encryption-key="$(openssl rand -hex 32)"
 
# Random token, used by the sandbox chart to auth back to the control plane.
kubectl create secret generic helix-runner-secrets \
  --from-literal=api-token="$(openssl rand -hex 32)"

Configure values.yaml

Save this as values.yaml for the chart:

# Production-ready configuration for helix-controlplane
# For more options: https://github.com/helixml/helix/blob/main/charts/helix-controlplane/values-example.yaml
 
global:
  # Public URL of your Helix deployment. Also injected into the desktop
  # sandbox containers as the URL their inner agent (Zed / Qwen Code)
  # uses to reach the control plane API. The default of
  # http://localhost:8080 is ONLY correct for a single-binary local
  # install - inside a Kubernetes cluster localhost resolves to the
  # sandbox pod itself and inference will fail with
  # "error sending request for url (http://localhost:8080/...)".
  #
  # In Kubernetes set this to either:
  #   - the cluster-internal service URL of the control plane, e.g.
  #     http://my-helix-controlplane.helix.svc.cluster.local
  #     (works for port-forward / private access)
  #   - or your public ingress hostname, e.g. https://helix.example.com
  serverUrl: http://my-helix-controlplane.helix.svc.cluster.local
 
# Leave image.tag unset so the chart uses its pinned appVersion.
# Do NOT set it to "latest" - no :latest tag is published to GHCR.
# To pin a specific release, use e.g. tag: "2.9.31".
 
searxng:
  enabled: true
 
chrome:
  enabled: true
 
pgvector:
  enabled: true
  auth:
    existingSecret: "pgvector-auth-secret"
    usernameKey: "username"
    passwordKey: "password"
    databaseKey: "database"
  persistence:
    enabled: true
    size: 50Gi
    storageClass: ""
    annotations: {}
    accessModes:
      - ReadWriteOnce
 
controlplane:
  licenseKeyExistingSecret: "helix-license"
  licenseKeyExistingSecretKey: "license-key"
 
  runnerTokenExistingSecret: "helix-runner-secrets"
  runnerTokenExistingSecretKey: "api-token"
 
  # Encryption key for secrets at rest (PATs, SSH keys, OAuth tokens).
  # Required - the API will refuse to start without it. Generated above
  # via `openssl rand -hex 32`. Do not rotate after data is written.
  encryptionKeyExistingSecret: "helix-encryption-key"
  encryptionKeyExistingSecretKey: "encryption-key"
 
  admin:
    userSource: "env"
    userIds: "all"
 
  haystack:
    enabled: true
    existingSecret: "helix-pgvector-creds"
    existingSecretDsnKey: "dsn"
    embeddingsModel: "MrLight/dse-qwen2-2b-mrl-v1"
    embeddingsDim: "1536"
    chunkSize: "1000"
    chunkOverlap: "50"
    chunkUnit: "word"
 
  rag:
    defaultProvider: "haystack"
    embeddingsProvider: "helix"
 
  inference:
    defaultProvider: "helix"
 
  fineTuning:
    defaultProvider: "helix"
 
  # Configure at least one LLM provider. Without this, there will be
  # no models available after first login. Provide an API key inline,
  # or reference a Kubernetes secret via controlplane.providers.<name>.apiKeyExistingSecret.
  # providers:
  #   anthropic:
  #     apiKey: "sk-ant-..."
  #   openai:
  #     apiKey: "sk-..."
 
  # Extra environment variables injected into the control plane pod.
  # The control plane process is where settings like HELIX_ENCODER
  # are read and forwarded to inner desktop containers spawned for
  # spec task / Zed agent sessions.
  #
  # Example: opt into hardware H.264 video encoding on AMD GPUs
  # (requires Helix 2.10.0+; no-op on 2.9.x). Default auto-detect uses
  # software OpenH264 on AMD because of a historical gst-va crash on
  # older Mesa; this flag bypasses that skip.
  # extraEnv:
  #   - name: HELIX_ENCODER
  #     value: "vaapi"
 
persistence:
  enabled: true
  size: 100Gi
  storageClass: ""
  accessModes:
    - ReadWriteOnce
 
volumes:
  - name: data
 
postgresql:
  enabled: true
  auth:
    existingSecret: "postgresql-auth-secret"
    usernameKey: "username"
    passwordKey: "password"
    databaseKey: "database"
  persistence:
    enabled: true
    size: 10Gi
  resources:
    requests:
      cpu: "100m"
      memory: "256Mi"
    limits:
      memory: "1Gi"
 
tika:
  enabled: false
 
typesense:
  enabled: false

External Postgres

The chart can use an existing managed Postgres instead of the bundled one, but the bundled pgvector sidecar requires the vectorchord and vectorchord-bm25 extensions. Managed services that ship stock Postgres (Azure Flexible Server, AWS RDS, Cloud SQL) do not include these extensions out of the box, so keep pgvector.enabled: true. That pod will host the RAG vector index independently of wherever your main Postgres lives.

Install the control plane

# Add Helix Helm repository
helm repo add helix https://charts.helixml.tech
helm repo update
 
# Install the control plane with your generated values.yaml and secrets.
# By default the chart uses its pinned appVersion. Do NOT set image.tag
# to "latest" - no :latest tag is published. To pin a specific release,
# add --set image.tag="<version>" (e.g. "2.9.31") or --version "<chart-version>".
helm upgrade --install my-helix-controlplane helix/helix-controlplane \
  -f values.yaml

First boot pulls the embedding model (~4 GB) into the Haystack sidecar; budget 5 to 10 minutes before the main pod becomes Ready.

External inference providers

Helix can route inference to external providers (OpenAI, Anthropic, Together, or any self-hosted OpenAI-compatible endpoint like vLLM) instead of running a local runner. Each provider is configured under controlplane.providers.* in the control-plane values. Use existingSecret for API keys so they stay out of your values file and git history.

First, create a Kubernetes Secret for each provider you want to use:

kubectl create secret generic openai-api-key \
  --from-literal=api-key="sk-..."
 
kubectl create secret generic anthropic-api-key \
  --from-literal=api-key="sk-ant-..."
 
kubectl create secret generic togetherai-api-key \
  --from-literal=api-key="..."

Then add a providers: block inside the controlplane: section of your values.yaml. Only include the providers you actually use:

controlplane:
  # ... your existing controlplane config ...
  providers:
    openai:
      existingSecret: "openai-api-key"
      existingSecretApiKeyKey: "api-key"
      # baseUrl: "https://api.openai.com/v1"   # override for Azure OpenAI or proxies
 
    anthropic:
      existingSecret: "anthropic-api-key"
      existingSecretApiKeyKey: "api-key"
 
    togetherai:
      existingSecret: "togetherai-api-key"
      existingSecretApiKeyKey: "api-key"
 
    vllm:
      # Self-hosted vLLM speaks the OpenAI API. Point at its cluster-internal service.
      # Most vLLM deployments need no API key; pass one via existingSecret if yours does.
      baseUrl: "http://my-vllm.vllm.svc.cluster.local:8000/v1"

Anthropic can also be used via GCP Vertex AI - set vertexProjectID, vertexRegion, and either vertexCredentialsJSON or vertexCredentialsSecret under providers.anthropic. See values.yaml in the chart for the full shape.

Re-run helm upgrade on the control plane to apply the change.

Install the sandbox chart (cloud desktops / Zed agent)

The helix-sandbox chart powers Helix's cloud desktop and in-browser Zed IDE features. It is optional; install it only if you want to run spec tasks or open the IDE in the browser.

The sandbox runs a Docker-in-Docker daemon which needs privileged pods, a GPU node pool (for desktop rendering), and large persistent volumes.

helm upgrade --install my-helix-sandbox helix/helix-sandbox \
  --namespace helix \
  --set sandbox.apiUrl="http://my-helix-controlplane.helix.svc.cluster.local" \
  --set sandbox.runnerTokenExistingSecret=helix-runner-secrets \
  --set sandbox.runnerTokenExistingSecretKey=api-token

Per-cloud override values are shipped with the chart: values-aks.yaml, values-eks.yaml, values-gke.yaml, values-bare-metal.yaml.

Access the control plane

kubectl port-forward -n helix svc/my-helix-controlplane 8080:80

Then open http://localhost:8080. The first registered user becomes an admin (because of controlplane.admin.userIds: "all").

For a production deployment you should configure an ingress with TLS termination and set global.serverUrl to the public URL. See the values-example.yaml for nginx + cert-manager, GKE managed-certificate, and cert-manager + DNS-01 examples.

Environment variables

Most options have first-class chart values - reach for those before resorting to env vars. The variables in this section are operator-tunable runtime knobs that don't have a structured chart key. They're passed through one of three extraEnv lists, depending on which component reads them:

ScopeWhere it's setApplies to
global.extraEnvhelix-controlplane chartcontrolplane + chrome + searxng pods. Use this for cluster-wide settings like HTTPS_PROXY / HTTP_PROXY / NO_PROXY that every outbound HTTP client needs.
controlplane.extraEnvhelix-controlplane chartcontrolplane pod only. Use this for desktop-bridge tunables (forwarded into dev containers) and control-plane process flags.
sandbox.extraEnvhelix-sandbox chartsandbox pod only. Use this for hydra-side flags read inside the sandbox before the dev container starts.
# helix-controlplane values.yaml
global:
  extraEnv:
    - name: HTTPS_PROXY
      value: "http://proxy.internal:8080"
    - name: NO_PROXY
      value: "localhost,127.0.0.1,.svc,.cluster.local"
 
controlplane:
  extraEnv:
    - name: HELIX_VIDEO_MODE
      value: shm

Re-run helm upgrade after changing any of these. The desktop-bridge env vars (video mode, encoder, GOP, render node) are read when a new dev container is created, so existing spec-task sessions keep their old values - start a new session to pick up the change.

Desktop streaming (controlplane.extraEnv)

These are read inside each spec-task / dev container by desktop-bridge. Helix 2.11.3+ forwards them from the controlplane env into dev containers automatically.

VariableDefaultNotes
HELIX_VIDEO_MODEzerocopyPipeWire capture path: zerocopy (DMA-BUF -> CUDA, fastest), native (DMA-BUF via GStreamer 1.24+), or shm (shared memory, slower but most compatible). Set to shm if the browser shows endless reconnects and desktop-bridge logs failed to set pipeline to playing after Received CUDA context via set_context - typically seen on managed-GPU clusters where the DMA-BUF -> CUDA import fails (e.g. GKE T4). NVENC is still used for the encode, so quality is unchanged.
HELIX_ENCODERauto-detectH.264 encoder selection: nvenc (NVIDIA), vaapi / vaapi-legacy (Intel/AMD), openh264 / x264 (software fallback). Useful when auto-detect picks the wrong encoder for your hardware.
HELIX_GOP_SIZE120GOP (Group of Pictures) size in frames. Default is 2 seconds at 60fps. Lower values make mid-stream joins faster (less catchup data) at the cost of slightly larger bitrate.
HELIX_RENDER_NODEunsetVA-API render device path (e.g. /dev/dri/renderD129). Set explicitly on multi-GPU hosts where auto-detection picks the wrong one. Set to SOFTWARE to disable VA-API.

Control plane behaviour (controlplane.extraEnv)

VariableDefaultNotes
HELIX_DISABLE_VERSION_CHECKunsetSet to any non-empty value to disable the upstream version-check ping on startup. Useful in air-gapped deployments.
HELIX_SKIP_AUTOMIGRATEunsetSet to 1 to skip GORM AutoMigrate on the public schema at boot. Only set this if you migrate the schema out-of-band - otherwise the control plane may crash on schema-mismatch errors.

Sandbox host (sandbox.extraEnv)

These are read by hydra inside the sandbox pod, before the dev container starts. Requires Helix 2.11.3+ for the sandbox.extraEnv plumbing.

VariableDefaultNotes
HELIX_SANDBOX_APT_MIRRORunsetOverride the Debian/Ubuntu APT mirror used when bootstrapping dev container images. Useful in air-gapped or geo-restricted environments.