Helm Configuration Options¶
Configuration options for the huddo and huddo-cp Helm charts.
How values are structured¶
Both charts are umbrella charts — every service is an instance of the same subchart, so the same keys are available under every service block.
| Key | Applies to |
|---|---|
global.env |
A map of environment variables added to every service |
global.envArray |
A list of valueFrom environment variables (secrets, ConfigMaps) for every service |
<service>.env |
Environment variables for one service, merged over global.env |
<service>.envArray |
valueFrom entries for one service |
<service>.replicaCount |
Number of pods for one service (default 2; wikishift, search-worker and attachments-indexer 1) |
<service>.image.tag |
Pin one service to a specific image tag |
<service>.ingress |
Ingress host, path, TLS and Traefik middleware for one service |
<service>.resources |
CPU/memory requests and limits |
Service names are core, acl, attachments, attachments-indexer, discussions, notifications, ideas, wikis, spaces, search, search-worker, editor, user, provider, licence, socketcluster, wikishift, and — when Boards is enabled — boards-core, boards-webfront, boards-app, boards-notification, boards-events. The huddo-cp chart adds s3; the huddo chart adds redis.
Warning
An entry in <service>.env overrides the same key from global.env, and both override the chart defaults. envArray entries shadowed by an env key of the same name are dropped.
Core options¶
| Key | Description |
|---|---|
global.repository |
Container registry and organisation. Default quay.io/huddo |
global.imageTag |
Image tag deployed for every service — see releases |
global.imagePullSecret |
Name of the Kubernetes image pull secret. Default quay |
global.env.HUDDO_URI |
Public base URL of Huddo, including the /huddo prefix and no trailing slash. Every app URL derives from it |
global.env.APP_URI |
Deprecated fallback for HUDDO_URI. When Boards runs in the same release, set this to the Boards UI URL |
global.env.API_GATEWAY |
Boards API URL. Only needed when Boards runs in the same release |
global.env.URL_BOARDS |
URL of Huddo Boards, shown in the Huddo app switcher. Set it whether Boards is in this release or a separate one |
global.env.SOCKET_CLUSTER_SECRET |
Shared secret signing real-time connections. Must be the same across every service |
global.env.SOCKET_CLUSTER_HOST |
In-cluster hostname of the SocketCluster worker. Derived from the release name — override only if you rename the release |
Products¶
| Key | Default | Description |
|---|---|---|
products.ideas.enabled |
true |
Deploys the ideas service |
products.wikis.enabled |
true |
Deploys the wikis service. editor is always deployed and is not gated |
products.spaces.enabled |
true |
Deploys the spaces service, Huddo Spaces |
products.search.enabled |
true |
Deploys cross-app search — search, search-worker and the OpenSearch cluster. See Search |
products.boards.enabled |
false |
Deploys Huddo Boards inside this release (boards-core, boards-webfront, boards-app, boards-notification, boards-events) |
global.drawio.enabled |
false |
Deploys the self-hosted draw.io editor behind the Wikis diagram block. See Diagrams |
wikishift.enabled |
false |
Deploys the Wikis migration utility |
activity-migration.enabled |
false |
huddo-cp only. Connections Activities ➡️ Boards migration |
Why draw.io sits under global
The other toggles live under products. Draw.io is one switch that has to move three things together — the editor deployment, its ingress, and the DRAWIO_ENABLED flag on the wikis app. That flag is environment on the wikis subchart, and a subchart can only read its own values plus global, so the switch lives there.
Shared services¶
The user, provider and licence services are shared between Huddo products. Both charts can either deploy them or alias them to an existing Huddo Boards release in the same namespace — see Adding Huddo to an existing Boards install.
| Key | Default | Description |
|---|---|---|
sharedServices.deploy |
true |
Deploy user, provider and licence. Set false when a Boards release already provides them |
sharedServices.aliases |
per chart | ExternalName targets used when deploy is false. Maps huddo-user, huddo-provider and huddo-licence onto the Boards service names |
The alias defaults already match the Boards chart each one pairs with, so they normally need no override:
| Chart | Pairs with | Default targets |
|---|---|---|
huddo-cp |
huddo-boards-cp |
boards-user, boards-provider, boards-licence |
huddo |
huddo-boards |
user, provider, licence |
Note
Rendering fails if products.boards.enabled is true while sharedServices.deploy is false — the two describe conflicting deployments.
MongoDB¶
huddo-cp reads the Component Pack MongoDB details from the connections-env ConfigMap and authenticates with X.509, so only MONGO_CERT_USER normally applies. The huddo chart needs the full connection details.
| Key | Description |
|---|---|
global.env.MONGO_PROTOCOL |
mongodb (default) or mongodb+srv |
global.env.MONGO_HOST |
host:port, or just the host for mongodb+srv |
global.env.MONGO_USER |
Database user |
global.env.MONGO_PASSWORD |
Database password |
global.env.MONGO_PARAMS |
Extra connection string query parameters, e.g. retryWrites=true&w=majority |
global.env.MONGO_REPLICA_SET_NAME |
Replica set name, appended to the query parameters |
global.env.MONGO_CERT_USER |
X.509 certificate user for MONGODB-X509 auth. huddo-cp defaults to kudos-boards; set to '' to use username/password auth instead |
<service>.env.MONGO_DB |
Database name for one service. Defaults are huddo-user, huddo-licence, huddo-attachments, huddo-discussions, huddo-notifications, huddo-ideas, huddo-wikis and huddo-spaces |
S3 file storage¶
The huddo-cp chart deploys SeaweedFS and wires these up for you — see S3 storage. The huddo chart expects an external endpoint.
| Key | Description |
|---|---|
global.env.S3_ENDPOINT |
Hostname of the S3 endpoint, no protocol |
global.env.S3_PORT |
Port, e.g. 443 |
global.env.S3_SECURE |
'true' to connect over TLS |
global.env.S3_USE_SSL |
As above, read by the Boards services |
global.env.S3_BUCKET |
Bucket name. Default huddo (Boards uses huddo-boards) |
global.env.S3_ACCESS_KEY |
Access key |
global.env.S3_SECRET_KEY |
Secret key |
Cache¶
huddo-cp binds the Component Pack cache automatically from the connections-env ConfigMap and the cache-service-secret / redis-secret secrets. The huddo chart deploys its own Redis (huddo-redis).
| Key | Description |
|---|---|
redis.enabled |
huddo chart only. Set false to use an existing cache |
global.env.REDIS_CACHE_HOST |
Cache hostname |
global.env.REDIS_CACHE_PORT |
Cache port |
global.env.REDIS_CACHE_PASSWORD |
Cache password, if required |
Search¶
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 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.
| Key | Default | Description |
|---|---|---|
products.search.enabled |
true |
Deploys search, search-worker and OpenSearch |
global.env.SEARCH_HOST |
http://huddo-search-internal |
In-cluster hostname of the search responder. Clear it alongside products.search.enabled=false, or every write logs a failed push to a host that no longer exists |
global.env.SEARCH_PORT |
23737 |
Responder port. Matches search.balancer.port |
search.ingress.path |
/search |
Public path for the query API (/search/api). Under a prefix, set /huddo/search with traefik.stripPrefix.prefix: /huddo |
search.env.OPENSEARCH_NODE |
http://huddo-opensearch:9200 |
OpenSearch endpoint. Point both search and search-worker at an external cluster to run one |
opensearch.persistence.size |
20Gi |
Index volume size |
opensearch.persistence.storageClassName |
— | Dynamic provisioner to claim from |
opensearch.persistence.nfs.server / .path |
— | NFS export to use instead of a provisioner |
opensearch.persistence.retainOnDelete |
true |
Annotates the claim helm.sh/resource-policy: keep, so helm uninstall leaves the index behind. See Keeping the index |
opensearch.nodeSelector / opensearch.tolerations |
— | Pin the cluster to a node pool of its own. See Giving OpenSearch room |
networkPolicy.enabled |
true |
NetworkPolicies restricting the responder (23737) to this release's apps and OpenSearch (9200) to search / search-worker |
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. 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
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.
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.
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. Installs on an NFS PV are unaffected either way, since that volume is Retain over an export that outlives the release.
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.
Per-service options¶
User¶
| Key | Description |
|---|---|
user.env.JWT_SECRET |
Recommended: random string of at least 32 characters used to sign sessions |
user.env.CONNECTIONS_* etc. |
Authentication provider settings — see below |
Licence¶
| Key | Description |
|---|---|
licence.env.LICENCE |
Licence key from the Huddo Store. Must cover the products you deploy (huddo.wikis, huddo.ideas) |
Notifications (email)¶
Email transport is chosen in this order: Ethereal (development preview) ➡️ SendGrid ➡️ generic SMTP. If none is configured, email delivery is disabled and everything else keeps working.
| Key | Description |
|---|---|
notifications.env.EMAIL_HOST |
SMTP gateway hostname |
notifications.env.EMAIL_PORT |
Optional: SMTP port. Default 25, or 587 when EMAIL_USERNAME is set |
notifications.env.EMAIL_USERNAME |
Optional: SMTP username. Setting it enables SMTP auth |
notifications.env.EMAIL_PASSWORD |
Optional: SMTP password |
notifications.env.EMAIL_FROM |
Address emails are sent from. Without it, delivery is skipped |
notifications.env.EMAIL_FROM_NAME |
Optional: display name on outbound email |
notifications.env.EMAIL_OPTIONS |
Optional: extra NodeMailer options as JSON, e.g. '{"requireTLS":true}'. If your SMTP gateway presents a private certificate, add it to the internal CA bundle rather than disabling verification here |
notifications.env.SENDGRID_API_KEY |
Optional: use SendGrid instead of SMTP |
notifications.env.EMAIL_ETHEREAL |
Optional: 'true' routes all mail to a throwaway Ethereal inbox for testing. Credentials are printed in the pod logs at startup |
ACL¶
| Key | Default | Description |
|---|---|---|
acl.env.L1_CACHE_TTL |
600 |
In-memory cache TTL in seconds |
acl.env.L2_CACHE_TTL |
3600 |
Redis cache TTL in seconds |
Attachments¶
| Key | Description |
|---|---|
attachments.env.UNSPLASH_ACCESS_KEY |
Optional: Unsplash access key. Without it, cover image search is disabled |
Wikis¶
| Key | Description |
|---|---|
wikis.env.NEXT_PUBLIC_BASE_PATH |
Path Wikis is served from. Set to match wikis.ingress.path (e.g. /huddo/wikis) |
Diagrams (draw.io)¶
Wikis has an inline diagram block backed by a self-hosted draw.io editor. The editor is a container of its own, framed by the wikis page and talking to it over postMessage only — no third-party framing, and no diagram content leaving the deployment.
Turn it on with the single switch, and give the ingress the same host Wikis is served from:
global:
drawio:
enabled: true
drawio:
ingress:
hosts:
- '[HUDDO_HOST]'
path: /huddo/drawio # /drawio where the release has no path prefix
| Key | Default | Description |
|---|---|---|
global.drawio.enabled |
false |
Deploys the editor, publishes it at drawio.ingress.path, and sets DRAWIO_ENABLED on wikis |
drawio.ingress.hosts |
— | Must be the same host as wikis.ingress.hosts |
drawio.ingress.path |
/drawio |
Include the release's path prefix, e.g. /huddo/drawio |
drawio.image |
jgraph/drawio |
Image reference. Point at your mirror for an air-gapped install |
drawio.imageTag |
pinned | Pinned deliberately — draw.io releases often and its embed protocol is not versioned |
drawio.imagePullSecret |
'' |
Only needed when pulling from a mirror that requires credentials |
drawio.networkPolicy.enabled |
true |
Denies egress off the cluster from the editor pod. In-cluster egress and DNS stay open |
Same origin is required, not cosmetic
Wikis derives the editor URL from HUDDO_URI, the same way it derives the editor and SocketCluster paths — so a release whose HUDDO_URI ends in /huddo looks for draw.io at /huddo/drawio. Serving it from another host reintroduces exactly the cross-origin framing and third-party-cookie problems that self-hosting exists to avoid.
stripPrefix defaults to the ingress path, so Tomcat keeps serving the editor at its own root whichever path you choose.
The image comes from Docker Hub
This is the only image in the stack that is not ours. The release's global.imagePullSecret holds Quay credentials and is deliberately not applied to this pod.
For an air-gapped or Component Pack install, mirror the image alongside the quay.io/huddo ones and point the chart at your copy. Anonymous Docker Hub pulls are rate-limited per source IP, so mirroring is worth it even where egress exists.
drawio:
image: registry.internal/mirror/jgraph/drawio
imagePullSecret: mirror-pull-secret
Turning it off again is safe
Gating is presentational only. The diagram node stays registered in the editor schema, so pages that already contain diagrams keep rendering them in both read and edit mode, and saving a page does not strip them — users simply cannot create or edit diagrams. Rendering fails if you enable draw.io with products.wikis.enabled: false, since nothing would frame it.
The pod is stateless: no database, no volume, no auth, no secrets. Wikis owns persistence — in embed mode the editor never talks to a server of its own.
Ingress¶
Every service block takes the same ingress options.
| Key | Description |
|---|---|
<service>.ingress.enabled |
Default true for services with a public route |
<service>.ingress.hosts |
List of hostnames (no protocol) |
<service>.ingress.path |
Path the service is served from |
<service>.ingress.pathType |
Default Prefix |
<service>.ingress.ingressClassName |
cnx-ingress-traefik in huddo-cp, traefik in huddo |
<service>.ingress.tls |
List of {hosts, secretName} — only if certificates are resolved in Kubernetes |
<service>.ingress.traefik.stripPrefix.enabled |
Creates a Traefik StripPrefix middleware and attaches it to the Ingress |
<service>.ingress.traefik.stripPrefix.prefix |
Prefix to strip. Defaults to ingress.path when blank — set it to /huddo for the services served under the shared prefix |
<service>.ingress.traefik.stickyCookie |
Session affinity for the services that need it (ideas, boards-core) |
socketcluster.ingress.prefixedPath.enabled |
Serves the WebSocket at /huddo/socketcluster as well as /socketcluster, so one front-proxy location /huddo block covers everything. On by default |
Warning
Do not also set traefik.ingress.kubernetes.io/router.middlewares in ingress.annotations — the chart injects it when stripPrefix or stickyCookie is enabled.
Secrets & certificates¶
These options control the secrets the chart can create or mount. All are optional — the chart behaves exactly as before when they are left unset.
Image pull secret¶
When registry, username and password are all set, the chart creates the image pull secret automatically, named after global.imagePullSecret so the deployments reference it with no further configuration. Leave them empty to manage the secret yourself. Supply the password via --set or an encrypted values file rather than committing it.
| Key | Description |
|---|---|
imageCredentials.registry |
Container registry hostname, e.g. quay.io |
imageCredentials.username |
Registry username |
imageCredentials.password |
Registry password / token |
Internal CA bundle¶
Mounts a CA bundle into every service and sets NODE_EXTRA_CA_CERTS so the apps trust internal TLS endpoints (a reverse proxy, MongoDB, S3, etc). Provide either existingSecret (you manage the secret) or pem (the chart creates it) — not both.
| Key | Description | Default |
|---|---|---|
global.internalCa.existingSecret |
Name of an existing secret holding the CA bundle to mount. The chart does not create or own it | — |
global.internalCa.pem |
Alternatively, the CA bundle (PEM) itself — the chart creates the secret named secretName from it |
— |
global.internalCa.key |
Filename/key within the secret. NODE_EXTRA_CA_CERTS is set to <mountPath>/<key> |
cert.pem |
global.internalCa.secretName |
Name of the secret to create when pem is set |
internal-ca |
global.internalCa.mountPath |
Directory the bundle is mounted at in every service | /etc/ssl/internal-ca |
Additional resources¶
| Key | Description |
|---|---|
extraObjects |
List of additional Kubernetes resource manifests to create alongside the chart (e.g. ConfigMaps, Secrets) |
Pod annotations¶
Adds annotations to the pods the chart creates — most commonly to opt Huddo into a service mesh such as Linkerd or Istio. All are empty by default, so leaving them unset renders exactly as before.
| Key | Description |
|---|---|
global.podAnnotations |
Annotations added to every pod in the release |
<service>.podAnnotations |
Annotations for one service only, merged on top of global.podAnnotations (the service's own keys win) |
s3.jobPodAnnotations |
huddo-cp only. Annotations for the S3 Job pods (bucket setup), layered last so they override the two above |
global:
podAnnotations:
linkerd.io/inject: enabled
core:
podAnnotations:
config.linkerd.io/proxy-cpu-limit: "1"
Exclude the S3 Job pods from the mesh
A meshed Job pod never completes — the proxy sidecar keeps running after the main container exits, so the Job hangs instead of finishing. If you enable mesh injection release-wide on huddo-cp, opt the S3 Jobs back out:
s3:
jobPodAnnotations:
linkerd.io/inject: disabled
Note
Changing these values changes the pod spec, so the affected pods are recreated on the next helm upgrade.
Provider specific options¶
Authentication is handled by the shared user service — the same service Huddo Boards uses. The provider keys are identical, under user.env:
Note
Register a separate OAuth client for Huddo with a callback of https://[HUDDO_URL]/auth/<provider>/callback — unless you are installing alongside an existing Boards release with sharedServices.deploy: false, in which case Huddo rides the Boards client and callback.
The provider service takes one Connections-specific option:
| Key | Description |
|---|---|
provider.env.WIDGET_ID |
Optional: ID of the Community widget configured in this step |
Logging¶
| Key | Description |
|---|---|
<service>.env.DEBUG |
Debug namespaces for the Huddo apps, e.g. 'wikis:*,shared:*,prisma:*' |
<service>.env.LOGGER_LEVEL |
Log level for the user, provider and licence services: error, warn, info, log, debug |