Skip to content

Search (OpenSearch)

Cross-app search is three pieces: the search API, a search-worker running the BullMQ index queue on the same image, and an OpenSearch cluster deployed as the huddo-opensearch subchart. It is on by default — turning it off leaves the in-app search boxes returning nothing.

The query API is served at /huddo/search/api. The other apps push documents to the search responder rather than calling the public API, so SEARCH_HOST points at an in-cluster ClusterIP service that is never exposed through the ingress.

Every option is listed under Configuration. Heap, memory and volume sizes are under Server Requirements.

Note

NetworkPolicies are only enforced by a CNI that implements them. Where the CNI ignores them, the ClusterIP boundary remains the floor — neither port is ever published through the ingress.

Health also matters on rollout: search reports not-ready with a 503 until OpenSearch is reachable, so a deploy waits for a serviceable index rather than serving empty results.


Storage

The index needs one PersistentVolume, 20Gi by default (opensearch.persistence.size). You have two options:

  1. Dynamic provisioning — the chart claims from your default storage class. Set opensearch.persistence.storageClassName to claim from another.
  2. NFS — the chart creates a PersistentVolume bound by name from opensearch.persistence.nfs.server and opensearch.persistence.nfs.path.

Storage before the first deploy

A Connections cluster usually has no dynamic provisioner, so the index needs an NFS export in the same layout as your other Component Pack volumes, prepared the same way as the S3 mount. Create the export before the first deploy — the chart's init container chowns it, but it will not create it.

opensearch:
  persistence:
    nfs:
      server: '[NFS_SERVER_IP]'
      path: /pv-connections/huddo-opensearch

Pod Security admission

OpenSearch needs the kernel setting vm.max_map_count to be at least 262144 on the node it runs on. The chart can set it with a privileged init container named sysctl, switched by opensearch.sysctlInitContainer.enabled.

A namespace that enforces the baseline or restricted Pod Security Standard rejects that container. helm upgrade prints the warning below, the OpenSearch pod is never created and search answers 503:

Warning: would violate PodSecurity "baseline:latest": privileged (container "sysctl" must not set securityContext.privileged=true)

To see what your namespace enforces, look for the pod-security.kubernetes.io/enforce label:

kubectl get namespace connections --show-labels

Component Pack's "Apply Pod security restrictions at the namespace level" step enforces baseline on the connections namespace. For that reason huddo-cp charts after 3.2.1 ship with the init container off. On huddo-cp 3.2.1 or earlier, and on a huddo install into a namespace that enforces a standard, turn it off in your config file:

opensearch:
  sysctlInitContainer:
    enabled: false

With the init container off, set the value yourself, as root, on every node that can run OpenSearch. The second command keeps it across reboots:

sysctl -w vm.max_map_count=262144
echo 'vm.max_map_count=262144' > /etc/sysctl.d/99-opensearch.conf

Component Pack's own OpenSearch needs the same setting, so nodes prepared for it usually have it already. A node pool you add for Huddo's OpenSearch (see Giving OpenSearch room) does not, until you set it. Check a node with sysctl vm.max_map_count.

Upgrading huddo-cp from 3.2.1 or earlier

If your namespace admits privileged containers, the init container has been setting vm.max_map_count at each pod start, and that value is lost when the node reboots. Before you upgrade, either set it on the nodes as above, or keep the init container with opensearch.sysctlInitContainer.enabled: true.


Giving OpenSearch room

OpenSearch requests 200m CPU and 2Gi of memory, with a 1g heap and a memory limit equal to its request. It is by some margin the largest pod in the release, and on a small cluster it is the one that will fail to schedule: if no single node has 2Gi free, the pod stays Pending, and because search reports not-ready until the index answers, the whole deploy waits and then times out.

Check what a node actually has free before the first deploy — kubectl describe node reports requested, not used, and the scheduler only looks at requests:

kubectl describe nodes | grep -A5 'Allocated resources'

Where the cluster is tight, give the index a node of its own. Label and taint a node pool, then point OpenSearch at it:

opensearch:
  nodeSelector:
    pool: search
  tolerations:
    - key: dedicated
      operator: Equal
      value: search
      effect: NoSchedule

The taint is what makes it stick — without one, other workloads fill the node and the index is homeless again. Pin OpenSearch alone: search and search-worker carry no resource requests and schedule anywhere, and leaving them on the general pool keeps search's two replicas spread by their pod anti-affinity, which a single-node pool would defeat.

Sizing note: a node advertises less than its machine type suggests. A 2 vCPU / 4 GB node typically offers around 940m CPU and 2.73Gi to the scheduler once the kubelet reservation is taken, and daemonsets take a few hundred Mi more — enough for a 2Gi pod, with little left for a larger heap.

Where the sysctl init container is off, a node pool you add this way needs vm.max_map_count set on it first. See Pod Security admission.


Keeping the index across an uninstall

The index claim is annotated helm.sh/resource-policy: keep by default, so helm uninstall leaves the PersistentVolumeClaim — and the data — in place. Reinstalling the same release name into the same namespace adopts the claim and finds the index intact.

This matters on a cloud provisioner: most default StorageClasses reclaim Delete, so without the annotation an uninstall would take the underlying disk with it. On NFS, the data is safe either way, because that volume is Retain over an export that outlives the release. From huddo-cp 2.3.0 and huddo 2.4.0, the chart also keeps the NFS PersistentVolume with its claim, so a reinstall with the same release name into the same namespace binds to it again. To install under a different release name or into a different namespace, first delete the kept claim and the PersistentVolume. If you do not, Helm stops the install.

Set opensearch.persistence.retainOnDelete: false to have the volume torn down with the release. The same switch exists on SeaweedFS as s3.persistence.retainOnDelete, where the stakes are higher — that volume holds attachment binaries rather than a rebuildable index.

Tip

The index is derived data. If it is ever lost, the worker rebuilds it from the service databases — you lose search results until the reindex completes, not content.


Using an OpenSearch cluster you already run

Set opensearch.enabled: false so the chart deploys no cluster, and point both search and search-worker at yours:

opensearch:
  enabled: false

search:
  env:
    OPENSEARCH_NODE: https://opensearch.example.com:9200

search-worker:
  env:
    OPENSEARCH_NODE: https://opensearch.example.com:9200
  • For a cluster of several nodes, set OPENSEARCH_NODES to a comma-separated list instead. It wins over OPENSEARCH_NODE.
  • For a cluster that runs the OpenSearch security plugin, add OPENSEARCH_USERNAME and OPENSEARCH_PASSWORD to both services.
  • For a cluster with a private certificate, add its CA to the internal CA bundle. OPENSEARCH_TLS_REJECT_UNAUTHORIZED: "false" accepts a self-signed certificate without one.

Leaving search out

Set products.search.enabled: false and clear global.env.SEARCH_HOST. Without the second, every write logs a failed push to a host that no longer exists.