App Suite Releases
  • 8.47
  • 8.35
  • 7.10.6
Imprint
  • 8.47
  • 8.35
  • 7.10.6
Imprint
  • Release 8.55Upcoming
    • Noteworthy Changes
      • Important Changes
      • App Suite Middleware
    • Changelogs
      • App Suite UI
      • App Suite Middleware
      • Additional Components
        • AI Service
        • Booking Service
        • OX Guard UI
        • OX Documents
        • Switchboard
        • UI Service
    • Helm Charts
      • Helm chart values
      • App Suite Stack Chart
      • Booking
      • Helm Chart core-cacheservice
      • Helm Chart core-documentconverter
      • Helm Chart core-documents-collaboration
      • Helm Chart core-imageconverter
      • core-mw
      • UI Service
      • Switchboard
  • Release 8.54
  • Release 8.53
  • Release 8.52
  • Release 8.51
  • Release 8.50
Maintained. Older releases are best effort.
Upcoming
Not released yet
LTS
Long-term support branch
LTS discontinued
No longer supported, upgrade recommended

Helm chart values

The OX AI-Service integrates 3rd party AIs in App Suite UI, currently ChatGPT from OpenAI and LocalAI compatible models.

Please check out the full documentation here: OX AI-Service documentation

Chart Configuration

ParameterDescriptionDefault
image.repositoryThe image to be used for the deploymentappsuite-core-internal/ai-service
image.pullPolicyThe imagePullPolicy for the deploymentIfNotPresent
image.tagThe image tag, defaults to app version""
defaultRegistryRegistry the image is pulled fromregistry.open-xchange.com
imagePullSecretsImage pull secrets, e.g. [{name: ai-service-pullsecret}][]
nameOverrideOverride the chart name""
fullnameOverrideOverride the full name of the release resources""
replicaCountNumber of pods; ignored when autoscaling is enabled1
containerPortContainer port of the HTTP API8080
resourcesContainer resource requests and limitsrequests: cpu 500m, memory 384Mi; limits: memory 512Mi
autoscaling.enabledCreate a HorizontalPodAutoscalerfalse
autoscaling.minReplicasMinimum number of pods1
autoscaling.maxReplicasMaximum number of pods100
autoscaling.targetMemoryUtilizationPercentageTarget memory utilization80
autoscaling.targetCPUUtilizationPercentageTarget CPU utilization; not set by default""
nodeSelectorNode selector for the pod{}
tolerationsTolerations for the pod[]
affinityAffinity rules for the pod{}
podAnnotationsAnnotations added to the podlogging.open-xchange.com/format: "appsuite-json"
probe.livenessLiveness probe on /live (port 9090); enabled plus any probe fieldenabled, periodSeconds 10, failureThreshold 15
probe.readinessReadiness probe on /ready (port 9090); enabled plus any probe fieldenabled, initialDelaySeconds 1, periodSeconds 5, failureThreshold 2, timeoutSeconds 5
service.typeKubernetes service typeClusterIP
service.portKubernetes service port80
bindAddrIP address to listen for connections"::"
originsAllowed origins for CORS*
logLevelspecify log level for service"info"
exposeApiDocsExpose API documentation via Swagger UI at /api-docsfalse
ingress.enabledGenerate ingress resourcefalse
ingress.classNameIngress class name""
ingress.annotationsMap of key-value pairs that will be added as annotations to the ingress resource{}
ingress.hostsHosts the ingress serves (- host: <name>)chart-example.local
ingress.tlsTLS configuration of the ingress[]
istio.enabledCreate an Istio VirtualServicefalse
istio.hostnameHost of the VirtualService""
istio.gatewayGateway the VirtualService binds to"istio-system/default-gateway"
istio.oauthProtect /admin with an oauth2-proxy registered as Istio ext_authz provider, route /oauth2 to it and label the pod for sidecar injectionfalse
istio.oauthProviderName of the ext_authz provider in the mesh config"oauth2-proxy"
istio.oauthHostnameService of the oauth2-proxy (port 4180)"oauth2-proxy.oauth2-proxy.svc.cluster.local"
oidc.issuerOIDC issuer URL, e.g. https://example.com/appsuite/ui""
openaiSecret.enabledEnable the secret for openaitrue
openaiAPIKeyOpenAI API Key""
openaiModerationModelOpenAI moderation model"omni-moderation-latest"
localaiSecret.enabledEnable the secret for localai. May be optional, depending on your used service and model.false
localaiAPIKeylocalAI API Key. May be optional, depending on your used service and model.""
mysqlSecret.enabledCreate the kubernetes secret for mysql (enable if you want to use the DB or provide own secret)true
overrides.mysqlSecretIf you provide your own secret for mysql put the secret name here""
overrides.openaiSecretIf you provide your own secret for OpenAI put the secret name here (key openaiAPIKey)""
overrides.awsSecretIf you provide your own secret for AWS put the secret name here (keys awsAccessKey, awsSecretKey)""
overrides.localaiSecretIf you provide your own secret for LocalAI put the secret name here (key localaiAPIKey)""
overrides.mcpSessionEncryptionSecretIf you provide your own secret for the MCP session encryption key put the secret name here (key mcpSessionEncryptionKey)""
mcpSessionEncryptionSecret.enabledCreate the kubernetes secret for the MCP session encryption key (from mcpSessionEncryptionKey)false
mcpSessionEncryptionKeyMCP session encryption key (AES-256-GCM seed); generate with openssl rand -hex 32""
mcp.allowedApiHostsComma-separated allow-list of Middleware hostnames for MCP auth (*.example.com wildcards supported); required when MCP is enabled""
mcp.rateLimitAuthPerMinRate limit for POST /api/mcp/auth per user per minute"5"
mcp.rateLimitToolsPerMinRate limit for MCP tool calls per user per minute"60"
mcp.maxConcurrentCallsMCP requests one user may have in flight at the same time (per pod)"4"
mcp.allowedOriginsComma-separated origins allowed to call POST /api/mcp/ from a browser; empty sends no CORS headers""
mcp.publicUrlMCP endpoint exactly as users enter it in a client, e.g. https://appsuite.example.com/api/ai/mcp (env MCP_PUBLIC_URL). OAuth for claude.ai and Claude Code is on only when this and mcp.oauthAppSuiteUrl are set; the discovery paths sit below this URL, and for claude.ai Anthropic's outbound range (see documentation/mcp.md) must reach the service""
mcp.oauthAppSuiteUrlApp Suite that shows the OAuth consent dialog, e.g. https://appsuite.example.com (env MCP_OAUTH_APP_SUITE_URL); set together with mcp.publicUrl""
mcp.maxGrantsPerUserConnections (OAuth clients plus manual keys) one user may hold (env MCP_MAX_GRANTS_PER_USER); rotating the MCP session encryption key invalidates registered OAuth client ids"10"
mcp.oauthAllowedRedirectsSecurity setting, recommended in production: comma-separated hostnames (https, exact match) and private-use schemes with their colon that OAuth clients may redirect to, e.g. claude.ai,cursor: (env MCP_OAUTH_ALLOWED_REDIRECTS); loopback is always allowed; empty allows every redirect, see documentation/mcp.md""
mcp.externalInvitationsCovers event invitations, mail recipients, vacation notice text, and text sent to external attendees or organizers (event and task text changes, the display name of an invited attendee, comments on replies, declines and cancellations), and booked resources whose address is outside the user's mail domains and their subdomains. false refuses addresses that are not listed as users in the global address book, and addresses the lookup cannot checkfalse
mcp.scopeTiersObject mapping the key tiers read, write and destructive to Middleware module scopes (env MCP_SCOPE_TIERS); a tier left out keeps the mapping derived from the tools; invalid scopes stop the service at startup{}
database.enabledUse Database. This is mandatory for AI chat, usage tracking, and rate limiting and therefore deprecated. With App Suite 8.34, the default changed to true.true
database.hostSQL server hostnameRELEASE-NAME-ai-service-sql
database.portSQL server port3306
database.nameDatabase nameRELEASE-NAME-ai-service
database.connectionsNumber of concurrent connections to the DB server"10"
database.userDB User with access rights to sqlDB""
database.passwordPassword of the database user""
migration.jobIdRotation token for the migrate Job name suffix (e.g. $CI_JOB_ID); falls back to release revision""
cron.cleanupDbDatabase cleanup interval (Cron notation)"0 4 * * *"
cron.sidecarInjection.disabledDisable Istio Sidecar Injection for CronJobstrue
openaiBaseUrlUrl of the OpenAI service (internal or localAI use only)""
localaiBaseUrlUrl of the localAI service. Falls back to to openaiBaseUrl if configured.""
aiEgressProxy.urlOutbound proxy for the AI provider APIs (OpenAI, LocalAI, Azure, AWS Bedrock)""
aiEgressProxy.noProxyHosts that stay direct; NO_PROXY syntax (host, .domain, *.domain, host:port, IP, *), evaluated the same way for every provider including Bedrock""
aiEgressProxy.secret.enabledRender aiEgressProxy.url into a secret; required when the url carries credentialsfalse
overrides.aiEgressProxySecretYour own secret for the proxy url (key aiEgressProxyUrl); needs secret.enabled=false""
extraCaCerts.certificatesInline PEM bundle of additional CA certificates; the chart creates the Secret""
extraCaCerts.existingSecretName of an existing Secret holding the PEM bundle; takes precedence over certificates""
extraCaCerts.keyKey inside the Secret that holds the bundle"ca.crt"
localaiModerationDisabledLegacy switch; LocalAI models are never moderated, regardless of this valuetrue
accounting.enabledEnable the accounting featurefalse
chatAssistantContextFirst (system) message that is sent to the ai chat to setup the mood and general context."You are a helpful AI assistant."
enforceDefaultModelAlways use the configured default model regardless of the clients requestfalse
disableAPIDisable all API endpoints. Only UI source files will be deliveredfalse
moderationDisabledDisable moderation for all models (testing only)false
awsSecret.enabledEnable the aws Secret: static keys (renders AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY) or a Bedrock API key (awsBearerToken); leave false for IRSA (serviceAccount.annotations) or EKS Pod Identity (association on serviceAccount.name)false
awsRegionRegion of the AWS user with Bedrock & Claude enabled; rendered whenever set, independent of awsSecret.enabled""
awsAccessKeyAWS IAM access key with Bedrock & Claude enabled (rendered as AWS_ACCESS_KEY_ID)""
awsSecretKeyAWS IAM secret key with Bedrock & Claude enabled (rendered as AWS_SECRET_ACCESS_KEY)""
awsBearerTokenBedrock API key, rendered as AWS_BEARER_TOKEN_BEDROCK from the aws Secret (third path next to static keys and IAM roles; needs awsSecret.enabled)""
awsClaudeUrlURL of the AWS Bedrock/Claude service (internal use only)""
bedrock.guardrail.idBedrock Guardrail identifier for output moderation; unset falls back to the classifier""
bedrock.guardrail.versionBedrock Guardrail version""
bedrock.moderationModelIdModel or inference profile ID for the classifier moderation fallback""
bedrock.moderationDisabledDisable Bedrock output moderation independently of moderationDisabledfalse
bedrock.moderationFailClosedRetract the answer in the UI ({reset}) when the Bedrock moderation call fails instead of delivering it unchecked; errors count in ai_moderation_errors_count either wayfalse
serviceAccount.createCreate a dedicated ServiceAccount for the pods; set false and name an existing one in serviceAccount.name when an operator already created the SA (e.g. for IRSA), otherwise the upgrade fails because the SA is not managed by Helmtrue
serviceAccount.nameName of the ServiceAccount; defaults to the chart fullname""
serviceAccount.annotationsAnnotations on the ServiceAccount, e.g. eks.amazonaws.com/role-arn for IRSA{}
serviceAccount.automountServiceAccountTokenSet on the ServiceAccount resource itselffalse
automountServiceAccountTokenPod-level token automount; this service never calls the Kubernetes APIfalse
basicAuth.enabledEnable the secret for basic authentication (Admin routes)false
basicAuth.userUsername for basic authentication (Admin routes)""
basicAuth.passwordPassword for basic authentication (Admin routes)""
overrides.basicAuthSecretIf you provide your own secret for basic auth put the secret name here""
bearerAuth.enabledEnable the secret for bearer authentication (Admin routes)false
bearerAuth.keysComma separated keys to use with bearer auth (Admin Routes)""
overrides.bearerAuthSecretIf you provide your own secret for bearer auth put the secret name here""
qdrant.enabledEnable Qdrant vector database for semantic searchfalse
qdrant.urlURL of the Qdrant instance""
qdrant.secret.enabledCreate the kubernetes secret for the Qdrant API key (from qdrant.apiKey)false
qdrant.apiKeyQdrant API Key (used when qdrant.secret.enabled is true)""
qdrant.timeoutMsTimeout in milliseconds for Qdrant requests""
qdrant.collectionNameCollection name used for mail embeddings"appsuite_mail_embeddings"
qdrant.distanceDistance metric for vector similarity"Cosine"
qdrant.mSizeHNSW M parameter for the main vector index"16"
qdrant.efConstructHNSW ef_construct parameter for index build"100"
qdrant.payloadMSizeHNSW M parameter for the payload index"16"
qdrant.embeddingsOnDiskStore vectors on disk in Qdrant"false"
qdrant.hnswIndexOnDiskStore the HNSW index on disk in Qdrant"false"
overrides.qdrantSecretIf you provide your own secret for Qdrant put the secret name here (key qdrantAPIKey)""
pgvector.enabledEnable PostgreSQL + pgvector as vector store (alternative to Qdrant, enable only one)false
pgvector.urlPostgreSQL connection string without the password""
pgvector.secret.enabledCreate the kubernetes secret for the pgvector password (from pgvector.password)false
pgvector.passwordPassword for the pgvector database user""
overrides.pgvectorSecretIf you provide your own secret for pgvector put the secret name here (key pgvectorPassword)""
openaiEmbeddingModelEmbedding model used for Smart Search when using OpenAI/localAI compatible embeddings""
embeddingSizeEmbedding vector size written to the vector store384
mailFetchConcurrencyNumber of concurrent mail fetch operations during embedding/backfill5
mailEmbedConcurrencyNumber of concurrent embedding operations during embedding/backfill2
embeddingQueueTtlMsTTL for embedding jobs in milliseconds7200000
embedRetryMaxMaximum number of retries for failed embedding jobs4
oauth2.enabledEnable OAuth2/OIDC authentication for the admin routesfalse
oauth2.domainDomain of the openid connect capable OAuth2 server""
plansList of plans with limits for users and brandsSee full documentation for details
modelsConfigList of models that the service supportsSee full documentation for details
configApplication configuration that is also relevent for the UI partsSee full documentation for details
monitoring.grafana.enabledEnable Grafana integration for dashboards and init jobfalse
monitoring.grafana.grafanaOperatorEnable Grafana Operator integration (adds label and skips Node.js job)false
monitoring.grafana.urlURL of the Grafana instance""
monitoring.grafana.secretNameName of the Kubernetes secret for Grafana credentials""
monitoring.grafana.usernameUsername for Grafana API access (if required)""
monitoring.grafana.passwordPassword for Grafana API access (if required)""
monitoring.grafana.apiTokenAPI token for Grafana (preferred for automation)""
monitoring.grafana.folderTarget folder in Grafana for dashboards""
monitoring.grafana.ttlInitJobTTL in seconds for the Grafana init job after completion600
monitoring.prometheus.urlURL of the Prometheus instance used for metrics collection""
monitoring.prometheus.dataSourceOverride the Prometheus datasource name in Grafana""
monitoring.prometheus.kube-prometheusEnable ServiceMonitor creation for kube-prometheus environmentsfalse
monitoring.prometheus.serviceMonitor.namespaceTarget namespace for ServiceMonitor""
monitoring.prometheus.serviceMonitor.intervalScrape interval for ServiceMonitor30s
monitoring.prometheus.serviceMonitor.scrapeTimeoutScrape timeout for ServiceMonitor""
monitoring.prometheus.serviceMonitor.honorLabelsHonor labels for ServiceMonitorfalse
monitoring.prometheus.serviceMonitor.additionalLabelsAdditional labels for ServiceMonitor{}
monitoring.prometheus.serviceMonitor.annotationsAnnotations for ServiceMonitor{}
monitoring.prometheus.serviceMonitor.relabelingsRelabeling configuration for ServiceMonitormaps __meta_kubernetes_pod_label_app_kubernetes_io_name to app
monitoring.prometheus.serviceMonitor.metricRelabelingsMetric relabeling configuration for ServiceMonitor[]

Job pod labels

The cleanup CronJob and the migration Job run outside the Deployment, so their pods do not carry the Deployment's pod labels (app.kubernetes.io/name plus instance is the Service selector). Kubernetes only adds job-name and controller-uid, which change on every run. To select the job pods in a NetworkPolicy, use the stable labels the chart sets:

podSelector:
  matchLabels:
    app.kubernetes.io/instance: as8          # your Helm release name
    app.kubernetes.io/component: cleanupdb   # or migratedb

AWS Bedrock credentials

claude-aws models pick up credentials from the AWS SDK default chain, so the chart supports four ways to deploy them:

  • Static keys: set awsSecret.enabled=true (or overrides.awsSecret for a pre-existing Secret) plus awsAccessKey/awsSecretKey, and awsRegion as a plain value. The pod gets AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY from the Secret.
  • Bedrock API key: set awsSecret.enabled=true and awsBearerToken; the pod gets AWS_BEARER_TOKEN_BEDROCK from the Secret and no static keys, even when awsAccessKey and awsSecretKey are still set (the token takes precedence, so stale keys cannot interfere). Use a long-term key bound to an IAM user: short-term keys expire after about 12 hours and are valid in one region only, after which every Bedrock call fails with an authentication error. With a pre-existing overrides.awsSecret only the static keys are read from it and awsBearerToken is ignored.
  • IRSA: leave awsSecret.enabled=false and set serviceAccount.annotations["eks.amazonaws.com/role-arn"] to the IAM role ARN. No static keys are rendered; the SDK picks up AWS_ROLE_ARN/AWS_WEB_IDENTITY_TOKEN_FILE that the EKS webhook injects into the ServiceAccount's pods.
  • EKS Pod Identity: leave awsSecret.enabled=false, no annotation; create the Pod Identity association for the chart's ServiceAccount (serviceAccount.name, default the chart fullname). The SDK reads the credentials from the container credential endpoint.

In both role-based modes awsRegion still needs to be set (or AWS_REGION supplied by the cluster's environment), since it is rendered independently of the secret.

Security Context

Security contexts follow the ox-common pattern with defaults for Wolfi-based images (nonroot UID 65532):

ParameterDescriptionDefault
podSecurityContextExplicit pod security context (overrides defaults){}
securityContextExplicit container security context (overrides defaults){}
defaultPodSecurityContext.fsGroupDefault pod fsGroup (Wolfi nonroot)65532
defaultSecurityContext.runAsNonRootDefault run as non-roottrue
defaultSecurityContext.runAsUserDefault user ID (Wolfi nonroot)65532
defaultSecurityContext.runAsGroupDefault group ID (Wolfi nonroot)65532
defaultSecurityContext.allowPrivilegeEscalationDefault prevent privilege escalationfalse
defaultSecurityContext.readOnlyRootFilesystemDefault read-only root filesystemtrue
defaultSecurityContext.capabilities.dropDefault dropped capabilities[ALL]
global.useDefaultSecurityContextEnable default security contextsfalse

To enable Wolfi security defaults, set global.useDefaultSecurityContext=true.

Next
App Suite Stack Chart