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, links, 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.
Environment variable order¶
You can set the same variable in more than one key. Each service gets the value from the key with the highest rank:
| Rank | Key | Set by |
|---|---|---|
| 1 | <service>.env |
You, for one service |
| 2 | <service>.envArray |
You, for one service |
| 3 | global.envArray |
You, for every service |
| 4 | global.env |
You, for every service. The chart also sets defaults here |
| 5 | <service>.defaultEnvArray |
The chart |
The huddo-cp chart keeps its per-service defaults in defaultEnvArray, defaultVolumes and defaultVolumeMounts. These hold the connections-env references and the mongo-secret volume. Your envArray, volumes and volumeMounts add to the defaults. Your entry replaces a default with the same name (for a volume mount, the same mountPath). To remove the defaults from a service, set defaultEnvArray: []. Clear defaultVolumes and defaultVolumeMounts together. If you clear only defaultVolumes, the chart stops with an error.
Note
Before huddo-cp 2.3.0 and huddo 2.4.0, global.env won over <service>.envArray and global.envArray.
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.AUTH_STORAGE_KEY |
localStorage key the browser keeps the Huddo token under. Default huddo-token. Set token, the Boards key, when Huddo shares a user service with Boards so a login in either product serves both — see Shared services |
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. Default huddo-socketcluster-worker, a fixed name whatever the release is called. If you set it to <release>-socketcluster-worker for an older chart, remove the override: the chart stops with an error until you do |
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.links.enabled |
true |
Deploys the links service. See Links |
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. Settings: WikiShift configuration |
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.
Whenever the two products share one user service — sharedServices.deploy: false, or Boards running in this release with products.boards.enabled: true — also set global.env.AUTH_STORAGE_KEY: token. Huddo then keeps the browser token under the localStorage key Boards uses, so a login in either product serves both. Each chart's values.yaml carries this line commented out next to the other global.env defaults.
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, huddo-spaces and huddo-links |
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 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.
| 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.env.OPENSEARCH_NODE |
http://huddo-opensearch:9200 |
OpenSearch endpoint. Point both search and search-worker at an external cluster to run one |
search.env.OPENSEARCH_NODES |
— | Comma-separated endpoints of a multi-node cluster. Wins over OPENSEARCH_NODE |
search.env.OPENSEARCH_USERNAME / OPENSEARCH_PASSWORD |
— | Basic auth for a cluster running the OpenSearch security plugin |
search.env.OPENSEARCH_TLS_REJECT_UNAUTHORIZED |
"true" |
"false" accepts a self-signed certificate. Prefer adding the CA to the internal CA bundle |
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. 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.
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 |
notifications.env.DEFAULT_LOCALE |
Optional: email language when the recipient's locale is unknown. Default en-AU. One of ar-SA, bg-BG, ca-ES, cs-CZ, da-DK, de-DE, el-GR, en-AU, en-US, es-ES, fa-IR, fi-FI, fr-FR, he-IL, hr-HR, hu-HU, it-IT, ja-JP, kk-KZ, ko-KR, nb-NO, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, sl-SI, sv-SE, th-TH, tr-TR, zh-CN, zh-TW; any other value stops the service from starting |
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 |
attachments-indexer.env.TEXT_INDEXER_CONCURRENCY |
Files the indexer reads text from at once. Default 2. Each extraction runs in its own process, so raise the indexer's memory limit with it |
attachments-indexer.env.OCR_ENABLED |
Read text in uploaded images (JPEG, PNG, WebP, AVIF) so search finds it. Default "true". Set "false" to turn OCR off |
attachments-indexer.env.OCR_LANGS |
Comma-separated languages for OCR. Default en,de,fr,es,it,nl. Supported: en, de, fr, es, it, nl, pt, pl, cs, da, sv, no (or nb), fi. An unsupported code stops the indexer from starting. Each extra language makes OCR slower |
attachments-indexer.env.OCR_MIN_CONFIDENCE |
Words OCR is less sure of than this (0–100) are dropped. Default 60 |
attachments-indexer.env.OCR_MAX_PIXELS |
Images with more pixels than this are not read. Default 40000000 |
attachments-indexer.env.TEXT_EXTRACT_MAX_BYTES |
Files larger than this, in bytes, are not read. Default 52428800 (50 MB) |
attachments-indexer.env.TEXT_MAX_CHARS |
Characters of text kept per file; the rest is not searchable. Default 200000 |
attachments-indexer.env.TEXT_EXTRACT_TIMEOUT_MS |
Time one file's read may take before it is stopped. Default 60000 |
attachments-indexer.env.TEXT_EXTRACT_MAX_MEMORY_MB |
Memory one file's read may use before it is stopped. Default 512 |
Ideas¶
The Huddo Boards automation creates a board when an idea is promoted. It needs both keys; without them, the automation records a failure instead of a board.
| Key | Description |
|---|---|
ideas.env.BOARDS_WEBHOOK_URL |
Boards' Ideas webhook, e.g. https://[BOARDS_HOST]/api-boards/webhook/ideas |
ideas.env.BOARDS_WEBHOOK_SECRET |
Shared secret that signs each call and its reply. Must equal IDEAS_WEBHOOK_SECRET on Boards core |
Wikis¶
Limits on whole-wiki exports and imports. Past a limit, users are told to export a section instead, or to try again later when the pod is busy.
| Key | Default | Description |
|---|---|---|
wikis.env.WIKIS_WHITEBOARDS_ENABLED |
"true" |
"false" turns whiteboards off |
wikis.env.WIKIS_PDF_MAX_PAGES |
300 |
Pages in one PDF export |
wikis.env.WIKIS_PDF_MAX_MB |
50 |
Stored page content in one PDF export, in MB |
wikis.env.WIKIS_PDF_MAX_SECONDS |
60 |
Time to build one PDF export |
wikis.env.WIKIS_PDF_CONCURRENCY |
2 |
PDF exports at once per pod |
wikis.env.WIKIS_DOCX_MAX_IMAGE_MB |
50 |
Image size one Word export embeds, in MB. Past it, the rest of the images show as a note |
wikis.env.WIKIS_ARCHIVE_MAX_PAGES |
2000 |
Pages in one wiki archive |
wikis.env.WIKIS_ARCHIVE_MAX_FILES |
5000 |
Attachments in one wiki archive |
wikis.env.WIKIS_ARCHIVE_MAX_BYTES |
134217728 |
Memory one archive export or import may hold, in bytes (128 MB). Raise the wikis memory limit with it |
wikis.env.WIKIS_ARCHIVE_MAX_ENTRY_BYTES |
16777216 |
Largest single entry in an imported archive, in bytes (16 MB) |
wikis.env.WIKIS_ARCHIVE_MAX_OUTPUT_BYTES |
1073741824 |
Size of one exported archive, attachments included, in bytes (1 GB) |
wikis.env.WIKIS_ARCHIVE_MAX_SECONDS |
600 |
Time one archive export or import may take |
wikis.env.WIKIS_ARCHIVE_CONCURRENCY |
1 |
Archive exports and imports at once per pod |
Links¶
Links is the store for every link across the apps: it fetches link previews, counts clicks and checks links against threat lists. The other apps push their links to it and acl routes link lookups through it, both over an in-cluster ClusterIP service that is never exposed through the ingress. The links pages are served at /huddo/links.
| Key | Default | Description |
|---|---|---|
products.links.enabled |
true |
Deploys links and its click key Secret |
global.env.LINKS_HOST |
http://huddo-links-internal |
In-cluster hostname of the links responder. Clear it alongside products.links.enabled=false; the apps then show plain links instead of preview cards |
global.env.LINKS_INTERNAL_PORT |
25465 |
Responder port. Matches links.balancer.port |
links.env.LINKS_TRUSTED_PROXY_HOPS |
'1' |
Number of proxies in front of links that append to X-Forwarded-For |
links.env.LINKS_REPUTATION_PROVIDER |
none |
Reputation lookup service. urlhaus makes reputation checks available to org admins. none keeps every link inside the install |
links.env.LINKS_URLHAUS_AUTH_KEY |
— | abuse.ch Auth-Key, required with urlhaus; without it links does not start. Take it from a Secret, see Links reputation checks |
links.env.LINKS_REPUTATION_MAX_AGE_DAYS |
7 |
Days a link's verdict stands before it is checked again |
links.env.LINKS_PREVIEW_TTL |
86400 |
Seconds a fetched link preview is reused before it is fetched again |
links.env.LINKS_CLICK_RETENTION_DAYS |
395 |
Days each click is kept. Click counts per link are kept for good |
links.env.LINKS_CLICK_TOKEN_HOURS |
24 |
Hours a click token from an open page stays valid. See Links click key |
links.env.LINKS_CONTINUE_TOKEN_MINUTES |
10 |
Minutes the Continue button on a link warning page stays valid |
links.env.LINKS_GEO_COUNTRY_HEADER |
— | A header with the visitor's ISO country code, set by a proxy in front of Huddo, e.g. cf-ipcountry. Read only when LINKS_TRUSTED_PROXY_HOPS is at least 1 |
links.env.LINKS_GEOIP_DB |
geoip/dbip-country-lite.mmdb |
Country or city database (.mmdb from DB-IP, MaxMind or IPinfo) used to place clicks. The image includes DB-IP Country Lite; to use another, mount it into the pod and set its path |
Set LINKS_TRUSTED_PROXY_HOPS to match your proxies
Links uses each click's address to place it (country, region, or a named network such as an office), to count a signed-out visitor once, and to rate-limit them. It reads the address from X-Forwarded-For, taking the entry this many places from the right, because each proxy appends the address it received the request from. Two settings must agree:
- Every proxy passes the header on. Front proxies append to it (for nginx, see Reverse Proxy). The ingress controller must also trust the addresses it receives traffic from: Traefik deletes
X-Forwarded-Forfrom any sender not inforwardedHeaders.trustedIPs. On the Traefik install (for Component Pack, the release installed with HCL's guide), setports.web.forwardedHeaders.trustedIPsandports.websecure.forwardedHeaders.trustedIPsto those addresses. With a NodePort Service andexternalTrafficPolicy: Cluster, they are the node IPs and, on an overlay network such as Calico IP-in-IP, the pod network range. LINKS_TRUSTED_PROXY_HOPScounts every proxy that appends: each front proxy (nginx, IHS, a cloud load balancer) plus the ingress controller. nginx in front of Traefik, as in Reverse Proxy, is'2'.
Too low, and every click is placed at one of your proxies. Too high, and a visitor can forge their address. If the ingress controller does not trust the front proxy, no value is right: every click carries a cluster address.
Admins upload threat list files of up to 50 MB. Traefik has no body size limit, but an nginx ingress controller defaults to 1 MB, so raise it on the links ingress:
links:
ingress:
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: 60m
A front proxy with its own limit, such as nginx's client_max_body_size, needs the same allowance for /huddo/links.
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. The editor is served at /huddo/drawio on global.ingress.hosts, the same host as Wikis:
global:
drawio:
enabled: true
| Key | Default | Description |
|---|---|---|
global.drawio.enabled |
false |
Deploys the editor, publishes it at /huddo/drawio, and sets DRAWIO_ENABLED on wikis |
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.
The chart strips /huddo/drawio before the request reaches the editor, so Tomcat keeps serving it at its own root.
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¶
Set the host once under global.ingress; every service uses it unless its own block overrides it.
| Key | Description |
|---|---|
global.ingress.hosts |
Hostnames (no protocol) for every service's Ingress. A service with no host anywhere gets no Ingress |
global.ingress.className |
Ingress class for every service: cnx-ingress-traefik in huddo-cp, traefik in huddo |
global.ingress.tls |
List of {hosts, secretName} — only if certificates are resolved in Kubernetes. Applies to services using global.ingress.hosts; a service with its own hosts sets its own tls |
Every service block takes the same ingress options, which win over global.ingress.
| Key | Description |
|---|---|
<service>.ingress.enabled |
Default true for services with a public route |
<service>.ingress.hosts |
List of hostnames (no protocol). Default global.ingress.hosts |
<service>.ingress.path |
Path the service is served from. Default /huddo for core, /huddo/<service> for the rest |
<service>.ingress.pathType |
Default Prefix |
<service>.ingress.ingressClassName |
Default global.ingress.className |
<service>.ingress.tls |
List of {hosts, secretName}. Default global.ingress.tls when the service uses the global hosts |
<service>.ingress.traefik.stripPrefix.enabled |
Creates a Traefik StripPrefix middleware and attaches it to the Ingress. On by default for attachments, discussions, notifications, search, editor and wikishift, which route without /huddo. Set false on an ingress controller other than Traefik |
<service>.ingress.traefik.stripPrefix.prefix |
Prefix to strip: /huddo where it is on by default; otherwise defaults to ingress.path when blank |
<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 |
Links reputation checks¶
Links can ask URLhaus whether each link is malicious, suspicious or safe. It is off unless you choose a provider, and then off for each organisation until one of its admins turns it on in Huddo Admin → Links, which also lists the organisation's flagged links. When it is on, links sends the full address of each link in that organisation to abuse.ch.
Get an Auth-Key from auth.abuse.ch, store it in a Secret, and add it to links. Your links.envArray replaces the chart's, so keep the click key entry in it, or links does not start:
links:
env:
LINKS_REPUTATION_PROVIDER: urlhaus
envArray:
- name: LINKS_CLICK_KEYS
valueFrom:
secretKeyRef:
name: huddo-links-secrets
key: LINKS_CLICK_KEYS
- name: LINKS_URLHAUS_AUTH_KEY
valueFrom:
secretKeyRef:
name: huddo-links-urlhaus
key: LINKS_URLHAUS_AUTH_KEY
Links click key¶
Links signs its click tokens with LINKS_CLICK_KEYS, read from the huddo-links-secrets Secret, and refuses to start without it. With linksSecret.create: true (the default) the chart generates a random key on first install and reuses it on every upgrade, so click tokens in pages that are already open stay valid. The Secret is kept when the release is uninstalled.
The chart finds the existing key with Helm's lookup, which returns nothing under helm template or a GitOps tool such as Argo CD or Flux. There, every render would make a new key, so set linksSecret.create: false and create the Secret yourself:
kubectl create secret generic huddo-links-secrets -n <namespace> --from-literal=LINKS_CLICK_KEYS="k1:$(openssl rand -base64 33)"
| Key | Default | Description |
|---|---|---|
linksSecret.create |
true |
Generate huddo-links-secrets. Set false to supply it yourself |
The value is id:base64secret, with a secret of at least 32 bytes. To rotate, put the new key first and keep the old one after a comma (k2:...,k1:...): links signs with the first key and still accepts tokens signed with the others. Drop the old key once its tokens have expired, after links.env.LINKS_CLICK_TOKEN_HOURS (default 24).
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 |