For the complete documentation index, see llms.txt. This page is also available as Markdown.

Systems Integrator Installation Guide

Cluster requirements, Helm values, and installation steps for systems integrators deploying DuploCloud AI HelpDesk on any Kubernetes cluster.

DuploCloud AI HelpDesk is distributed as a single Helm chart and runs on any conformant Kubernetes cluster. The Kubernetes distribution is irrelevant — Amazon EKS, Azure AKS, Google GKE, self-managed Kubernetes on cloud VMs, and self-managed Kubernetes on-premises are all supported, as long as the cluster meets the requirements in this guide.

How this guide is organized

Section
Purpose

What must exist before helm install

How to configure the chart for your environment

Install the chart, create DNS records, verify, and configure the platform

Worked examples per platform — informational, not prescriptive

Common symptoms and fixes

What to collect before contacting DuploCloud

Removing the release and what it leaves behind

Replace every value in angle brackets, such as <ADMIN_EMAIL>, before applying commands or values files. Do not commit credentials, OAuth secrets, or MongoDB passwords to source control.

Obtain from DuploCloud before you begin

  • The Helm chart version to install.

  • A license token. Without one the platform starts in a fail-closed state — see 1.12 License token.

  • Access to the chart registry if your network restricts outbound access to quay.io.

# Inspect the latest published chart
helm show chart oci://quay.io/duplocloud/helpdesk

# Inspect a specific version
helm show chart oci://quay.io/duplocloud/helpdesk --version <VERSION>

1. Requirements

Everything in this section must be in place before running helm install. The chart creates Kubernetes resources such as Ingress and PersistentVolumeClaim objects, but it does not install the controllers and drivers that fulfill them. Providing those is the integrator's responsibility.

1.1 Kubernetes cluster

Requirement
Detail

Distribution

Any conformant Kubernetes distribution — managed (EKS, AKS, GKE) or self-managed (cloud VMs or on-premises)

Version

1.26 or later (tested through 1.36)

Architecture

amd64 (x86-64) or arm64 nodes

Nodes

Minimum 2 for high availability; autoscaling to 6 or more recommended

Node size

2 vCPU / 4 GiB minimum per node. Plan a baseline of roughly 2.1 vCPU and 2.2 GiB for the stack, excluding workload growth

Access

A kubeconfig able to create the release namespace and all namespaced resources in it (Deployments, StatefulSets, Services, Ingress, PersistentVolumeClaims, Secrets, ConfigMaps, ServiceAccounts, CronJobs). The chart creates no cluster-scoped resources; cluster-wide permissions are needed only if you enable the optional bundled NFS server, which installs a StorageClass and ClusterRole

Tooling

kubectl 1.26 or later and Helm 3.12 or later on the machine performing the install

1.2 Ingress controller

The chart creates Ingress resources for the application, the web terminal, and (optionally) an internal endpoint for the agent. The cluster must provide a Layer 7 ingress controller that supports:

  • Host-based routing

  • WebSocket upgrades

  • Request bodies of at least 50 MiB

  • Proxy buffers of at least 16 KiB (OAuth callbacks carry large headers)

  • Long read/send timeouts (3600 seconds recommended) for interactive agent sessions

Any controller that meets these requirements works. Set ingress.className and ingress.annotations to match the controller you choose. Examples that have been validated: AWS Load Balancer Controller (alb), ingress-nginx (nginx), and the GKE Gateway API (gateway.enabled: true).

1.3 Shared storage (ReadWriteMany)

The backend and the agent share files, so each needs a volume that can be mounted by more than one pod. The cluster must provide a StorageClass capable of provisioning ReadWriteMany (RWX) persistent volumes with:

Requirement
Detail

Access mode

ReadWriteMany

Filesystem

POSIX semantics; files owned by UID/GID 1000 must be readable and writable by the pods

Capacity

At least 10 GiB each for backend.persistence and duploAgent.persistence

Reclaim policy

Retain recommended, so data survives accidental PVC deletion

Volume expansion

Recommended (allowVolumeExpansion: true)

Node prerequisites

If the storage is NFS-based, NFS client utilities (for example nfs-common) must be installed on every node

Anything that satisfies these properties is acceptable — a cloud file service (Amazon EFS, Azure Files NFS, Google Filestore), an existing NFS appliance or server in your datacenter, a distributed filesystem (CephFS, Longhorn RWX, Portworx Sharedv4), or an NFS subdir provisioner backed by storage you already run.

1.4 Block storage (ReadWriteOnce)

MongoDB, the MongoDB backup job, and ClickHouse (if enabled) each need a ReadWriteOnce (RWO) volume. Most clusters provide this through a default StorageClass; verify one exists, or set mongodb.persistence.storageClass, mongodbBackup.storage.storageClass, and clickhouse.persistence.storageClass explicitly.

Volume
Minimum size
Notes

MongoDB data

8 GiB

Pin to a zone if your block storage is zonal

MongoDB backups

25 GiB

Uses WaitForFirstConsumer; see the note on --wait below

ClickHouse (optional)

20 GiB

Only when clickhouse.enabled: true

1.5 Cluster autoscaler

If the node pool autoscales, a cluster autoscaler must be installed and configured for the cluster. This is not needed for fixed-size node pools.

1.6 TLS certificate

HTTPS is mandatory. Provide a certificate from a trusted certificate authority that covers both the application hostname and the web-terminal (xterm) hostname, using either:

  • A cloud-managed certificate (for example ACM, Google-managed, or Azure Key Vault) referenced through ingress annotations, or

  • A Kubernetes TLS Secret in the release namespace — for example issued through cert-manager — referenced through ingress.tls.

1.7 DNS

Two hostnames are required: one for the application and one for the web terminal (xterm.hostname). Create the DNS records after helm install, because the load-balancer address is only available once the ingress controller has reconciled the Ingress.

1.8 Identity provider

At least one of Google, Microsoft Entra, Okta, or Keycloak. Register the application with the redirect URI https://<APP_HOSTNAME>/signin-<provider> and set the matching chart values:

Provider
Redirect URI suffix
Helm values

Google

/signin-google

secrets.googleClientId, secrets.googleClientSecret

Microsoft

/signin-microsoft

secrets.microsoftClientId, secrets.microsoftClientSecret, secrets.microsoftTenantId

Okta

/signin-okta

secrets.oktaClientId, secrets.oktaClientSecret, secrets.oktaDomain

Keycloak

/signin-keycloak

secrets.keycloakAuthority, secrets.keycloakClientId, secrets.keycloakClientSecret, secrets.keycloakRealm, secrets.keycloakHost

Set config.authAllowedOrigins to the exact public frontend origin, including the scheme, and configure at least one superuser in config.authSuperUsers before the first login.

1.9 LLM provider

Choose one. The provider does not need to run in the same cloud as the cluster — an on-premises cluster can use Amazon Bedrock with static credentials, for example.

Provider

What is needed

CLAUDE_MODEL format

Amazon Bedrock

IAM credentials (IRSA on EKS, or static access keys elsewhere) allowing bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream on both foundation-model/* and inference-profile/* resources

us.anthropic.claude-sonnet-4-6

Azure AI Foundry

Foundry endpoint plus a managed identity or API key

claude-sonnet-4-6

GCP Vertex AI

Claude enabled in the Vertex AI Model Garden (not yet fully supported)

claude-sonnet-4-6

Anthropic direct

API key (sk-ant-...)

claude-sonnet-4-6

1.10 Network egress

Pods need outbound access to:

  • quay.io (container images and the Helm chart), plus any other registries you configure

  • The chosen LLM provider's API endpoints

  • The identity provider

Air-gapped deployment is not validated by this guide. Contact DuploCloud if you need to mirror images into a private registry.

1.11 Agent container privileges

The agent runs each ticket in an isolated sandbox and needs the SYS_ADMIN and NET_ADMIN Linux capabilities (the chart default). On some platforms the container must additionally run as root with privileged: true — see Tested configurations. Clusters enforcing Pod Security Admission need an exemption for the release namespace. Validate this with the cluster's security team before a production rollout.

1.12 License token

A license token issued by DuploCloud is required. Without a valid license the platform starts but runs fail-closed: users can sign in, but creating tickets, workspaces, providers, users, and other licensed resources is refused until a license is applied. Request the token from DuploCloud alongside the chart version.

There are two ways to apply it:

  • Helm values — set secrets.licensingToken in values.yaml before install (or supply the Licensing__Token key yourself when you manage secrets externally — see 2.2).

  • From the UI — after install, paste the token under AI Admin → Access Control → License → Apply New License. It takes effect immediately with no restart.

See License for how licensing works, the UI walkthrough, and troubleshooting. Treat the token like any other credential and keep it out of source control.


2. Helm values reference

All configuration lives in a single values.yaml.

2.1 Required values

Key
Description
Example

config.authFrontendBaseUrl

Public URL of the app. Used for OAuth redirects and CORS.

https://helpdesk.example.com

config.authAllowedOrigins

CORS allowed origin. Must match authFrontendBaseUrl exactly. Single origin only.

https://helpdesk.example.com

config.authSuperUsers

Comma-separated emails granted super-admin on first login.

admin@example.com

config.infraRegion

Cloud region. Used for Bedrock endpoint routing.

us-west-2

secrets.licensingToken

License token issued by DuploCloud. Without it the platform starts fail-closed; an admin can still sign in and apply a license from the UI.

<LICENSE_TOKEN>

mongodb.auth.rootPassword

MongoDB root password.

openssl rand -hex 16

xterm.hostname

Hostname for the web terminal (separate ingress rule).

xterm.example.com

2.2 Auto-generated secrets

These generate automatically on first install and persist across upgrades. Do not set them unless you need deterministic values.

Key
Behavior
Warning

secrets.jwtSharedSecret

Auto-generated (48-char alphanumeric). Persists via Secret lookup on upgrade.

secrets.encryptionMasterKey

Auto-generated (96 bytes base64). Persists via Secret lookup on upgrade.

Never change after first install — makes all encrypted data unrecoverable.

External secret management: set secrets.existingSecret to the name of a pre-existing Kubernetes Secret to skip chart-managed secret creation entirely. The external Secret must contain all required keys, including Licensing__Token (see the chart's templates/secret.yaml for exact key names). Use this with Vault, External Secrets Operator, or Sealed Secrets.

2.3 Identity / SSO

See 1.8 Identity provider for redirect URI patterns. Set the values for your chosen provider(s):

Optional IdP group sync:

2.4 Storage

Point the chart at the StorageClass names you provisioned in 1.3 and 1.4.

Key
Description
Default

backend.persistence.enabled

Enable the shared RWX volume

true

backend.persistence.storageClass

RWX StorageClass name

"" (cluster default)

backend.persistence.size

RWX volume size

10Gi

backend.persistence.accessModes

Must be [ReadWriteMany]

[ReadWriteMany]

duploAgent.persistence.storageClass

Agent's own PVC StorageClass. Set to the RWX class to avoid WaitForFirstConsumer issues.

""

mongodb.persistence.size

MongoDB data volume

8Gi

mongodb.persistence.storageClass

RWO StorageClass

"" (cluster default)

mongodbBackup.enabled

Enable the backup CronJob

true

mongodbBackup.schedule

Cron schedule

0 0 * * *

mongodbBackup.storage.size

Backup volume size

25Gi

nfs-server.enabled

Enable the bundled NFS server (evaluation only; see 1.3)

false

nfs-server.storageClass.name

StorageClass name created by the bundled NFS server

nfs

2.5 Ingress

Key
Description
Default

ingress.enabled

Enable the external Ingress

true

ingress.className

Ingress class of the controller you installed

alb

ingress.annotations

Controller-specific annotations (certificate reference, buffer size, body size, timeouts)

{}

ingress.tls

TLS config. Leave empty when TLS terminates at an upstream load balancer.

[]

internalIngress.enabled

Enable an internal-only Ingress for the agent

true

internalIngress.className

Must match your internal ingress class

alb

gateway.enabled

Use the Kubernetes Gateway API instead of Ingress (GKE)

false

gateway.className

GatewayClass name

gke-l7-global-external-managed

Whatever controller you use, make sure its configuration satisfies the limits in 1.2. For ingress-nginx, that looks like:

2.6 LLM provider

For clusters on EKS with an OIDC provider. The IAM policy must allow foundation-model/* and inference-profile/*.

For any cluster not on AWS — on-premises, AKS, GKE, or self-managed.

2.7 Components

Component
Default
Key

Backend

always

–

Frontend

always

–

MongoDB

true

mongodb.enabled

Agent

true

duploAgent.enabled

Helpdesk MCP

true

helpdeskMcp.enabled

Core MCP

false

coreMcp.enabled

Slack Backend

true

slackBackend.enabled

Teams Backend

false

teamsBackend.enabled

XTerm

true

xterm.enabled

Bedrock Subscription

true

bedrockSubscription.enabled

Collector

false

collector.enabled

NFS Server

false

nfs-server.enabled

ClickHouse

false

clickhouse.enabled

Internal Ingress

true

internalIngress.enabled

Gateway API

false

gateway.enabled

2.8 Tolerations and scheduling

The chart ships dedicated=hd:NoSchedule as the default toleration via a YAML anchor. Subcharts (MongoDB, NFS server, ClickHouse) do not inherit values from the parent, so overrides must be repeated on each.

Scenario
Configuration

Dedicated tainted nodes

Leave the default — tolerations match automatically

Shared cluster (no taints)

Override tolerations: [] at the top level and on each subchart (mongodb.tolerations: [], nfs-server.tolerations: [])

To use a different taint key and keep the subcharts aligned:

2.9 Reference values file

A platform-neutral starting point. Replace all <PLACEHOLDER> values before use, then layer on the ingress annotations and LLM settings for your environment. Check it against helm show values for your chart version — keys can be added, renamed, or removed between releases.


3. Install and configure

3.1 Install the Helm chart

3.2 Create DNS records

After install, read the load-balancer address and create DNS records:

Create CNAME (or A) records for both hostnames pointing to this address.

3.3 Verify infrastructure

3.4 Post-install platform configuration

After pods are healthy and login works, configure the platform through the UI. Each step links to the page that covers it in detail.

Step
Where
Key details
Reference

1. Login

Browser → app URL

Sign in as a config.authSuperUsers email via your IdP

2. Verify license

AI Admin → Access Control → License

Status should read Active. If not, apply the token here.

3. Create LLM Model

AI Admin → LLMs → + Add

Model ID must match the CLAUDE_MODEL env var exactly

4. Create Agent

AI Admin → Agents → + Add

Endpoint: http://<RELEASE>-duplo-agent:8000 (base URL only). Set endpointDetails.path: api/sendMessage. Set metaData.STREAMING_ENABLED: true.

5. Create LLM Mapping

LLMs → LLM Mappings → + Add

Map model + agent pair. Scope: Workspace. Target: your workspace.

6. Create Skill

AI Admin → Skills → + Add

Provide name + markdown description (skillMd field)

7. Create Persona

AI Admin → Personas → + Add

Link skill(s)

8. Create Provider

AI Admin → Providers → + Add

Connect the cloud account or Kubernetes cluster the agent will operate on

9. Create Scope

During provider creation

Links credentials to a named scope

10. Create Workspace

AI Admin → Workspaces → + Add

Link persona(s), then add agent and scope(s)

11. Test

AI DevOps → select workspace → create ticket

Verify the agent responds with LLM-generated content


4. Tested configurations

The configurations below are ones DuploCloud has validated end to end. They are worked examples, not requirements. Any cluster that meets Section 1 is supported, regardless of distribution or which products you choose to satisfy each requirement. The Helm values and post-install steps are the same everywhere — only the items listed here differ.

Topic
Detail

Tested

2026-09-15, Kubernetes 1.35

Ingress

AWS Load Balancer Controller. ingress.className: alb with ALB annotations (certificate ARN, scheme, subnets, target-type).

RWX storage

Amazon EFS via the EFS CSI driver, StorageClass with provisioningMode: efs-ap. The EFS CSI controller needs its own IRSA role.

RWO storage

EBS CSI driver (required as a separate add-on on Kubernetes 1.35+). Mark gp2 or gp3 as the default StorageClass.

LLM

Amazon Bedrock via IRSA. IAM policy must include foundation-model/* and inference-profile/* resources.

Agent sandbox

SYS_ADMIN + NET_ADMIN capabilities work without privileged: true.

OIDC

Must be enabled on the cluster for IRSA.

Topic
Detail

Tested

2026-09-16, Kubernetes 1.35

Ingress

ingress-nginx installed via Helm; Azure Load Balancer assigns a public IP.

RWX storage

Azure Files NFS via the Azure Files CSI driver.

LLM

Azure AI Foundry (recommended) or Amazon Bedrock via static keys.

Agent sandbox

The agent runs as UID 1001 by default — override with runAsUser: 0 + privileged: true for the sandbox to work. Capabilities are dropped for non-root users even with privileged set.

Topic
Detail

Tested

2026-09-17, Kubernetes 1.35

Ingress

ingress-nginx installed via Helm, or the GKE Gateway API natively (gateway.enabled: true).

RWX storage

Google Filestore via the Filestore CSI driver.

LLM

Vertex AI (when Claude is enabled in your project) or Amazon Bedrock via static keys.

Agent sandbox

Same as AKS — runAsUser: 0 + privileged: true required.

GCP cloud provider

Not yet implemented. The Kubernetes scope works for cluster operations. GCP-native resource queries (Compute Engine, GCS, etc.) are not supported.

Applies to any Kubernetes you operate yourself, whatever the distribution or where the nodes run.

Topic
Detail

Tested

2026-09-16, Kubernetes 1.36

Ingress

ingress-nginx. On clusters without a cloud load balancer, run the controller with hostNetwork: true as a DaemonSet, or front it with your own load balancer (MetalLB, hardware appliance, and so on).

RWX storage

Any RWX-capable StorageClass — an existing NFS appliance or server, CephFS, Longhorn, Portworx, or similar. If NFS-based, install NFS client utilities on every node. The chart's bundled NFS server is acceptable for evaluation only.

RWO storage

Any RWO StorageClass your distribution provides (local-path provisioner, Longhorn, SAN-backed CSI, and so on). Set it as the cluster default or reference it explicitly.

LLM

Amazon Bedrock via static keys, Azure AI Foundry, or the Anthropic API directly.

Agent sandbox

privileged: true + runAsUser: 0 required.

Agent sandbox override

Where a platform requires the agent to run as root, set it in values rather than patching the deployment after install, so upgrades preserve it:


5. Troubleshooting

Symptom
Cause
Fix

Creates refused (tickets, workspaces)

No valid license in force — missing, expired, or issued for a different host

Apply the license from AI Admin → Access Control → License, or set secrets.licensingToken and upgrade the release

Pods Pending on tolerations

Node taint doesn't match the chart default dedicated=hd:NoSchedule

Set tolerations: [] in values (and on each subchart) if no taints are used

Pods Pending on capacity

Insufficient node resources

Check node capacity and autoscaler configuration

PVC stuck Pending

No matching StorageClass, or WaitForFirstConsumer with no consumer

Verify the StorageClass exists and supports the required access mode; for the backup PVC, don't use --wait

RWX mount fails (bad option)

NFS client not installed on the node

Install NFS client utilities on every node (for example nfs-common on Debian/Ubuntu)

MongoDB cannot schedule

RWO volume pinned to a different zone than available nodes

Check StorageClass zone affinity

bwrap permission denied

Agent needs SYS_ADMIN as root

Set runAsUser: 0, privileged: true on the agent container

OAuth redirect error (400)

Wrong redirect URI in the IdP

Must be https://<HOST>/signin-<provider> exactly; CORS origin must match exactly

502 Bad Gateway on login

Proxy buffer too small for auth headers

Raise the ingress proxy buffer size to at least 16 KiB

413 or header errors

Ingress body size or buffer limits

Allow a 50 MiB request body and a 16 KiB proxy buffer

LLM AccessDeniedException

Bedrock IAM missing inference-profile/*

Add both foundation-model/* and inference-profile/* to the IAM policy

Wrong CLAUDE_MODEL format

Model ID doesn't match the provider

Bedrock: us.anthropic.claude-*; Azure/Anthropic: claude-*

Agent 404 on ticket

Agent endpoint includes the full path

Endpoint should be the base URL only; set endpointDetails.path: api/sendMessage

LLM dropdown empty in ticket UI

No LLM Model + Mapping configured

Create Model → LLM Mapping → link to workspace (3.4)

Image pull failures

No egress to quay.io or required registries

Allow outbound access or mirror images

helm install --wait timeout

Backup PVC WaitForFirstConsumer

Don't use --wait on initial install

Encrypted data unreadable

encryptionMasterKey was changed

Never change this key after first install


6. Handoff and escalation

When a step fails, collect:

Include the chart version, Kubernetes version and distribution, cloud or datacenter environment, sanitized values file, relevant events, and pod logs.

Channel
Use when

DuploCloud Slack #si-support

First-line support; most issues resolved here

DuploCloud support portal

Formal ticket with SLA tracking

GitHub Issues (duplocloud/helpdesk)

Bug reports with reproduction steps


7. Uninstall

What uninstall leaves behind:

  • Secrets with helm.sh/resource-policy: keep (JWT shared secret, encryption key)

  • The backup PVC (retained by the Retain reclaim policy)

Clean up manually if performing a full teardown. Preserve the encryptionMasterKey secret if you intend to restore data later.

Last updated

Was this helpful?