Installation on On-premises / vanilla Kubernetes

Install CAST Imaging on an on-premises or vanilla Kubernetes cluster using Helm charts

Overview

This guide covers the installation of CAST Imaging on an on-premises (or otherwise non-managed) Kubernetes cluster (i.e. a “vanilla” Kubernetes distribution that you operate yourself) using Helm charts.

The same CAST Imaging Helm chart is used for both cloud-managed clusters (EKS / AKS / GKE) and on-premises clusters. The main differences are:

  • The K8SProvider variable in values.yaml must be set to K8S.
  • The cluster does not provide managed storage classes, load balancer integration, or ingress controllers out of the box. You are responsible for selecting, installing, and operating these building blocks on your cluster (CSI storage drivers, a load-balancer implementation, an ingress / gateway controller, etc.). The CAST Imaging chart simply consumes whatever you have installed.

This page focuses on the on-premises specifics. For everything else (Keycloak parameters, initial start-up configuration, authentication, start/stop scripts, external PostgreSQL, storage locations, etc.) the chart behaves identically to the cloud-managed variants and the same documentation applies.

Requirements

  • Access to Docker Hub registry - CAST Imaging Docker images are available as listed in the table below
  • A clone of the latest release branch from the Git repository containing the Helm chart scripts: git clone https://github.com/CAST-Extend/com.castsoftware.castimaging-v3.kubernetessetup (to clone an older release, add the “-b x.x.x” flag with the desired release number).
  • A valid CAST Imaging License
  • A working Kubernetes cluster with, at minimum:
    • Kubernetes version 1.31 or higher
    • CSI storage drivers installed by your cluster administrator, exposing at least one block / disk storage class and one shared file storage class (required for the shared-datadir volume, which must be accessible by multiple Imaging services). CAST Imaging is agnostic to the specific driver (e.g. local-path-provisioner, longhorn, or vsphere-csi for block/disk; nfs-csi for shared file; rook-ceph for either), provided it meets the following permission requirements (setting this up is the cluster administrator’s responsibility): CAST Imaging pods run as non-root users and rely on the Kubernetes fsGroup security context setting to make their volumes writable. The storage classes must therefore support fsGroup. If your driver does not support it (typically an NFS export with root_squash), the volumes must be pre-provisioned writable by the UID/GID of the pods that use them: see Volume permissions when the storage driver does not support fsGroup.
    • A load-balancer implementation such as MetalLB external link. Without one, services will remain in <pending> state for their external IP. Setting this up is the cluster administrator’s responsibility.
    • (Optional) an ingress / gateway controller (NGINX Ingress, NGINX Gateway Fabric, Istio, etc.) if you want HTTPS access. See Step 3.
  • Optional setup choices:
    • Use an external PostgreSQL instance (by default, an embedded PostgreSQL instance is deployed)
    • Enable the analysis node autoscaler to automatically add and remove analysis nodes based on the job queue (≥ 3.6.9, see Analysis node autoscaling)

Docker images

CAST Imaging is provided in a set of Docker images as follows:

CAST Imaging component Image name URL
imaging-services Gateway castimaging/gateway external link
imaging-services Control Panel castimaging/admin-center external link
imaging-services SSO Service castimaging/sso-service external link
imaging-services Auth Service castimaging/auth-service external link
imaging-services Console castimaging/console external link
dashboards Dashboards castimaging/dashboards-v3 external link
analysis-node Analysis Node castimaging/analysis-node external link
imaging-viewer ETL castimaging/etl-service external link
imaging-viewer AI Service castimaging/ai-service external link
imaging-viewer API Service castimaging/imaging-apis external link
imaging-viewer Viewer Server castimaging/viewer external link
imaging-viewer Neo4j castimaging/neo4j external link
imaging-viewer MCP Server castimaging/imaging-mcp-server external link
extend-local-server Extend Proxy castimaging/extend-proxy external link
utilities Init Container castimaging/init-util external link

Installation process

Before starting the installation, ensure that your Kubernetes cluster is running, all the CAST Imaging docker images are available in the registry and that helm and kubectl are installed on your system.

Step 1 - Cluster prerequisites

  • Make sure your Kubernetes cluster is up and that kubectl is configured to point at it:
$ kubectl get nodes

You can list the storage classes available on your cluster with:

$ kubectl get storageclass

Step 2 - Prepare and run the CAST Imaging installation

  • Review and adjust the parameter values in the values.yaml file (located at the root of the cloned Git repository branch) in between the section separated with # marks.

  • Set the K8SProvider: option to K8S:

K8SProvider: K8S
  • Storage classes: disable storage-class creation by the chart and point CAST Imaging at the storage classes already installed on your cluster:
CreateStorageClass: false
DiskClassName: <your-disk-storage-class>   # e.g. local-path, longhorn, rook-ceph-block, ...
FileClassName: <your-file-storage-class>   # e.g. nfs-csi, rook-cephfs, ...  (must support ReadWriteMany, even with a single analysis-node)
  • Run helm-install.bat|sh (depending on your base OS) located at the root of the cloned Git repository branch

Keycloak component parameters

CAST Imaging uses the Keycloak system to manage all authentication requirements (see Authentication for more information, and Keycloak accounts and passwords for an explanation of each account provided and how to change its password). You should take note of two parameters that you may wish to customize before running the installation:

KeycloakAdminPassword: <default-password>
KCADMIN_USERNAME: kcadmin
  • KeycloakAdminPassword defines the password for the Keycloak admin “service” account used for intra-component communication. Set this if you wish to define the password yourself, e.g. by running echo -n "my-new-password" | base64 to generate a new encrypted password. In releases up to 3.6.5-funcrel this password defaults to “admin” (encrypted in base64);

    Updated in ≥ 3.6.6-funcrel
    a fresh installation generates a random password when you do not set one. This password can also be updated post install (see below).

  • KCADMIN_USERNAME defines the name of the account (kcadmin by default with password “admin”) used to login in to the Keycloak UI to administer authentication. Update kcadmin to your preferred account name. Note that this change can only be done BEFORE a new installation.

Step 3 - Configure network settings for console-gateway (main entrypoint) and extendproxy (optional) services

By default, with CreateLoadBalancer: true and none of the ingress/gateway flags enabled, the chart deploys the console-gateway service as a Kubernetes Service of type LoadBalancer, accessible over HTTP on port 8090. For this to result in an actual external IP, a load-balancer implementation such as MetalLB external link must be available on the cluster. Without one, the service’s external IP stays <pending>; you can still reach the service via NodePort or by port-forwarding for testing.

If you want HTTPS access, use one of the three options below (they are the same as for cloud-managed Kubernetes). Choose one of the three options below (Gateway API, NGINX Ingress, or Istio). All three options share a common Final steps subsection at the end.


Option A: If you want to use a Gateway API implementation

The Gateway API external link is the modern, vendor-neutral successor to the Kubernetes Ingress API. CAST Imaging provides a GatewayAPI.yaml template that creates the Gateway and HTTPRoute resources needed to expose console-gateway-service and (optionally) extendproxy. The template has been tested with NGINX Gateway Fabric external link (NGF), but the Gateway / HTTPRoute resources are portable to any conformant implementation (Istio, Envoy Gateway, etc.).

  • Set UseGatewayAPI: true in values.yaml:
UseGatewayAPI: true
  • Install the Gateway API CRDs on the cluster (if not already installed):
$ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.1/standard-install.yaml
  • Install a Gateway controller. For NGINX Gateway Fabric:
$ helm install ngf oci://ghcr.io/nginx/charts/nginx-gateway-fabric --create-namespace -n nginx-gateway

Verify the installation:

$ kubectl get gatewayclass
$ kubectl get pods -n nginx-gateway
  • Configure the Gateway resource in values.yaml. Two options are supported:

    • Option 1 - Create a new Gateway dedicated to CAST Imaging (default):
    Gateway:
      create: true
      className: nginx     # Use "nginx" for NGF, or the gatewayClassName of your controller
    
    • Option 2 - Attach to an existing shared Gateway already deployed on the cluster:
    Gateway:
      create: false
      name: my-shared-gateway
      namespace: my-gateway-namespace
    

    When attaching to an existing Gateway, it must already define listeners named https (and https-extend if ExtendProxy.enable is true with a different hostname for the extend proxy). The shared Gateway’s listener must also allow routes from the CAST Imaging namespace (allowedRoutes.namespaces.from: All or a matching Selector).

  • Create TLS Secret(s) using the certificate files associated to the DNS name(s) you are planning to use. The secrets must be created in the CAST Imaging namespace (Gateway listeners read certificates from the Gateway’s own namespace, unless a ReferenceGrant is used):

$ kubectl create secret tls tls-secret-cast --cert=my-cert-folder\myhostname.com\fullchain.pem --key=my-cert-folder\myhostname.com\privkey.pem -n castimaging-v3
# Optional (only needed if you are deploying ExtendProxy):
$ kubectl create secret tls tls-secret-cast-extend --cert=my-cert-folder\myextendhostname.com\fullchain.pem --key=my-cert-folder\myextendhostname.com\privkey.pem -n castimaging-v3
# (fullchain.pem <=> tls.crt ; privkey.pem <=> tls.key)

If you are deploying ExtendProxy (optional) and want to use the same hostname for both services, just create the 2 secrets using the same certificate files.

➡️ Continue with the Final steps (Gateway API, Istio or NGINX Ingress) subsection below.


Option B: If you want to use a Kubernetes NGINX Ingress

  • Set CreateIngress: true in values.yaml:
CreateIngress: true
  • Install the Ingress driver on the cluster:
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
$ helm repo update
$ helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx --create-namespace
  • Create TLS Secret(s) using the certificate files associated to the DNS name(s) you are planning to use:
$ kubectl create secret tls tls-secret-cast --cert=my-cert-folder\myhostname.com\fullchain.pem --key=my-cert-folder\myhostname.com\privkey.pem -n castimaging-v3
# Optional (only needed if you are deploying ExtendProxy):
$ kubectl create secret tls tls-secret-cast-extend --cert=my-cert-folder\myextendhostname.com\fullchain.pem --key=my-cert-folder\myextendhostname.com\privkey.pem -n castimaging-v3
# (fullchain.pem <=> tls.crt ; privkey.pem <=> tls.key)

If you want to use the same hostname for both services, just create the 2 secrets using the same certificate files.

➡️ Continue with the Final steps (Gateway API, Istio or NGINX Ingress) subsection below.


Option C: If you want to use an Istio Ingress Gateway

  • Set CreateIstioGateway: true in values.yaml:
CreateIstioGateway: true

Note that Istio may be already installed on your cluster. Please check with your administrator.

  • Install Istio on the cluster (Linux/Mac):
$ curl -L https://istio.io/downloadIstio | sh -
$ cd istio-*
$ export PATH=$PWD/bin:$PATH
$ istioctl install --set profile=default -y
  • Install Istio on the cluster (Windows - PowerShell using Chocolatey):
choco install istioctl
istioctl install --set profile=default -y
  • Install Istio on the cluster (Windows - PowerShell using manual download and install of a specific version)
$ISTIO_VERSION="1.30.1"  # Check https://github.com/istio/istio/releases for latest
Invoke-WebRequest -Uri "https://github.com/istio/istio/releases/download/$ISTIO_VERSION/istio-$ISTIO_VERSION-win.zip" -OutFile "istio.zip"
Expand-Archive -Path "istio.zip" -DestinationPath "." -Force
cd "istio-$ISTIO_VERSION"
$env:PATH = "$PWD\bin;$env:PATH"
istioctl install --set profile=default -y
  • Optional - If you want to use a specific Istio Gateway already deployed on your cluster, set the gateway name in ExistingIstioGatewayName:
UseIstioGateway: true
ExistingIstioGatewayName: "some-namespace/my-gateway"
  • Create TLS Secret(s) using the certificate files associated to the DNS name(s) you are planning to use (to be created in the istio-system namespace):
$ kubectl create secret tls tls-secret-cast --cert=my-cert-folder\myhostname.com\fullchain.pem --key=my-cert-folder\myhostname.com\privkey.pem -n istio-system
# Optional (only needed if you are deploying ExtendProxy):
$ kubectl create secret tls tls-secret-cast-extend --cert=my-cert-folder\myextendhostname.com\fullchain.pem --key=my-cert-folder\myextendhostname.com\privkey.pem -n istio-system
# (fullchain.pem <=> tls.crt ; privkey.pem <=> tls.key)

If you want to use the same hostname for both services, just create the 2 secrets using the same certificate files.

➡️ Continue with the Final steps (Gateway API, Istio or NGINX Ingress) subsection below.


Final steps (Gateway API, Istio or NGINX Ingress)

Optional - When Gateway API, Istio or NGINX Ingress is implemented to access the console-gateway service with a certificate that cannot be verified (e.g., self-signed certificate or internal CA), the certificate will need to be stored in CAST auth-service to avoid certificate validation errors:

  • set: UseCustomTrustStore: true in values.yaml
  • Insert the encoded certificate:
    • directly inside the auth.caCertificate variable in values.yaml
    • or using helm upgrade ... --set-file auth.caCertificate=ca.crt ... to override the variable value with the ca.crt file content
UseCustomTrustStore: true
auth:
  caCertificate: |
    -----BEGIN CERTIFICATE-----
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    -----END CERTIFICATE-----

If you are also deploying ExtendProxy (optional), choose one of the two sub-options below:

  • Set hostnames in values.yaml - same-hostname-option: use the same hostname for all services
FrontEndHost: https://myhostname.com
ExtendProxy
  enable: true
  exthostname: myhostname.com

Exposed URLs will be:

  • https://myhostname.com (or https://myhostname.com/mycontext if ContextUrl is enabled)

  • https://myhostname.com/extendproxy

  • Set hostnames in values.yaml - different-hostname-option: use a different hostname for each service:

FrontEndHost: https://myhostname.com
ExtendProxy
  enable: true
  exthostname: myextendhostname.com

Exposed URLs will be:

  • https://myhostname.com (or https://myhostname.com/mycontext if ContextUrl is enabled)

  • https://myextendhostname.com/extendproxy

  • Apply the helm chart changes by running helm-upgrade.bat|sh (depending on your base OS) located at the root of the cloned Git repository branch.

  • Create a DNS record pointing at the reverse proxy external IP address. To retrieve the external IP:

# For NGINX Gateway Fabric, use this command:
$ kubectl get gateways -n castimaging-v3
#
# For an NGINX Ingress, use this command:
$ kubectl get ingress -n castimaging-v3
#
# For an Istio Ingress, use this command:
$ kubectl get service istio-ingressgateway -n istio-system

Step 4 - Install Extend Local Server (optional)

To install Extend Local Server:

  • In values.yaml (located at the root of the cloned Git repository branch), set ExtendProxy.enable to true and update the ExtendProxy.exthostname variable with the DNS name. In case you want to access it thru its Load Balancer (set “CreateLoadBalancer: true”), use the LoadBalancer external IP address instead or the DNS name (kubectl get service -n castimaging-v3 extendproxy to get the IP):
ExtendProxy:
    enable: true
    exthostname:  myextendhost.com # or LoadBalancer external IP address
  • Run helm-upgrade.bat|sh (depending on your base OS) located at the root of the cloned Git repository branch.
  • Review the log of the extendproxy pod to find the Extend Local Server administration URL and API key (these are required for managing Extend Local Server and configuring CAST Imaging to use it - you can find out more about this in Extend Local Server). You can get the extendproxy pod name by running kubectl get pods -n castimaging-v3 then run kubectl logs -n castimaging-v3 castextend-xxxxxxxx to display the log.
  • In Console System Settings, use this value for the “CAST Extend URL”: http://extendproxy:8085

Step 5 - Initial start up configuration

License and Extend API key steps can be skipped if values were provided for these helm chart variables:

  • ImagingLicenseType
  • ImagingLicense
  • ExtendApiKey

If not, when the install is complete, browse to the public/external URL (on AWS, the FrontEndHost URL) and login using the default local admin/admin credentials.

You will then be prompted to configure:

  • your licensing strategy. Choose either a Named Application strategy (where each application you onboard requires a dedicated license key entered when you perform the onboarding), or a Contributing Developers strategy (a global license key based on the number of users):

License key

  • CAST Extend settings / Proxy settings (if you chose to install Extend Local Server (see Step 4 above) then you now need to input the URL and API key so that CAST Imaging uses it).

CAST Extend settings

As a final check, browse to the URL below and ensure that you have at least one CAST Imaging Node Service, the CAST Dashboards and the CAST Imaging Viewer components listed:

https://<public or external URL>/admin/services

Services

Step 6 - Configure authentication

Out-of-the-box, CAST Imaging is configured to use Local Authentication via a simple username/password system managed by Keycloak. Default login credentials for CAST Imaging are provided (admin/admin) with the global ADMIN profile so that installation can be set up initially.

CAST recommends configuring CAST Imaging (via the Keycloak component) to use your on-premises enterprise authentication system such as LDAP or SAML Single Sign-on instead before you start to onboard applications. To access the Keycloak UI to configure your authentication system, use the kcadmin account. See Authentication for more information, or Keycloak accounts and passwords for an explanation of each account and how to change its password.

Updating the Keycloak admin service account password post installation

Out-of-the-box Keycloak is provisioned with a service account used for intra-component communication, with the username admin. Its password is set by the KeycloakAdminPassword parameter during installation. If you need to change this password post-installation, follow the steps below:

Change the password via the UI

  1. Navigate to the Keycloak account security page using the following URL (replace <public-external-url> with your installation’s external URL). This is the self-service account page for the admin service account, not the Keycloak admin console:
https://<public-external-url>/auth/realms/master/account/account-security/signing-in
  1. Log in as the admin service account, using its current password
  2. Update the password through the account settings interface

Update the password in the Helm Chart

After changing the password in the Keycloak UI, you must update the Helm chart configuration to match.

  1. Encode your new password in base64 format. On a Linux machine, run:
$ echo -n "my-new-password" | base64

  1. In your cloned Git repository containing the CAST Imaging Kubernetes installation files, open:
values.yaml
  1. Locate this line:
KeycloakAdminPassword: <default-password>
  1. Replace the defined password with your base64-encoded password:
KeycloakAdminPassword: <your-base64-encoded-password>
  1. Example: If your base64 output is bXktbmV3LXBhc3N3b3Jk, the line should read:
KeycloakAdminPassword: bXktbmV3LXBhc3N3b3Jk

Apply changes

Run Util-ScaleDownAll.bat and helm-upgrade.bat to restart CAST Imaging and apply the changes.

How to start and stop CAST Imaging

Use the following script files (located at the root of the cloned Git repository branch) to stop and start CAST Imaging:

  • Util-ScaleDownAll.bat|sh
  • Util-ScaleUpAll.bat|sh

Storage class selection

The FileClassName you set in values.yaml must point to a storage class backed by a CSI driver supporting ReadWriteMany (RWX) access mode, because the shared pvc-shared-datadir volume is mounted at the same time by the analysis node(s), console-service and console-control-panel. RWX is therefore required even with a single analysis node. Examples of RWX-capable on-premises options include NFS-based CSI drivers, CephFS (via Rook), GlusterFS, etc. A standard block-storage driver (e.g. local-path-provisioner, plain iSCSI) only supports ReadWriteOnce (RWO) and cannot be used for FileClassName.

Volume permissions when the storage driver does not support fsGroup

CAST Imaging pods run as fixed, non-root users. When a pod starts, Kubernetes uses its fsGroup setting to change the group ownership of the volume so that the pod can write to it (with fsGroupChangePolicy: OnRootMismatch, this is only done when the root folder of the volume does not already have the expected ownership). Some drivers do not apply fsGroup, and NFS servers configured with root_squash (the default) refuse the ownership change. In that case, pods fail to start or fail when writing their files (Permission denied in the pod logs), and the storage administrator must prepare the volumes so that they are owned by the UID/GID used by the pods:

Volume (PVC) Used by UID:GID
pvc-shared-datadir (shared, FileClassName) analysis nodes, console-service, control-panel 10001:10001
castdir-console-analysis-node-<n> analysis nodes 10001:10001
pvc-consoleservice-logs, pvc-controlpanel-logs console-service, control-panel 10001:10001
pvc-authenticationservice-logs, pvc-gatewayservice-logs, pvc-ssoservice-logs authentication, gateway and SSO services 65532:65532
pvc-dashboards-logs dashboards 65534:65534
db-data embedded PostgreSQL 999:999
neo4jdata-viewer-neo4j-0 Neo4j 7474:7474
pvc-extendproxy-data ExtendProxy 1001:1001
pvc-imagingviewer-v3-*, pvc-mcpserver-logs, pvc-mcpserver-data Viewer services, MCP server 1000:1000

Block / disk drivers formatting the volume with a standard Linux filesystem (ext4, xfs) support fsGroup, so this situation mostly concerns the shared pvc-shared-datadir volume when it is backed by NFS. The example below shows how to pre-provision it on an NFS server, using a static PersistentVolume that the chart’s pvc-shared-datadir claim binds to.

  1. On the NFS server, as root, create the folder, give it to UID/GID 10001, and set the setgid bit so that files and folders created inside it keep the 10001 group:

    mkdir -p /exports/castimaging/shared-datadir
    chown -R 10001:10001 /exports/castimaging/shared-datadir
    chmod 2770 /exports/castimaging/shared-datadir
    
    # Check the result: owner and group must be 10001, permissions drwxrws---
    ls -ldn /exports/castimaging/shared-datadir
    

    UID/GID 10001 does not need to exist as a user on the NFS server or on the Kubernetes nodes: only the numeric IDs matter.

  2. Export the folder to the Kubernetes nodes. Keep root_squash (the default), as the pods do not run as root. For example, in /etc/exports:

    /exports/castimaging/shared-datadir  10.0.0.0/24(rw,sync,no_subtree_check,root_squash)
    

    Replace 10.0.0.0/24 by the network of your Kubernetes nodes, then reload the exports:

    exportfs -ra
    exportfs -v    # check that the folder is exported with the expected options
    

    If your NFS server uses all_squash, also add anonuid=10001,anongid=10001 to the export options so that all files are created as 10001:10001.

  3. Before running the installation, create a PersistentVolume pointing to this folder. Its storageClassName must be the value of FileClassName in values.yaml, its capacity at least size_shared_datadir (default 100Gi), and its claimRef must target the pvc-shared-datadir claim in the CAST Imaging namespace (default castimaging-v3):

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv-castimaging-shared-datadir
    spec:
      capacity:
        storage: 100Gi
      volumeMode: Filesystem
      accessModes:
        - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain
      storageClassName: <your-file-storage-class>   # same value as FileClassName
      mountOptions:
        - nfsvers=4.1
      nfs:
        server: <nfs-server-name-or-ip>
        path: /exports/castimaging/shared-datadir
      claimRef:
        namespace: castimaging-v3
        name: pvc-shared-datadir
    
    kubectl apply -f pv-castimaging-shared-datadir.yaml
    

    After the installation, kubectl get pvc pvc-shared-datadir -n castimaging-v3 must show the claim as Bound to pv-castimaging-shared-datadir.

    This PersistentVolume uses the NFS volume type built into Kubernetes, so no CSI driver is needed, and the storage class named in storageClassName does not need a provisioner. However:

    • The NFS client tools must be installed on every node that can run a CAST Imaging pod (nfs-common on Debian/Ubuntu, nfs-utils on RHEL/Rocky/SUSE), because the volume is mounted by the kubelet from the node itself. Without them, the pods remain in ContainerCreating with a mount failed error in kubectl describe pod.
    • nfsvers=4.1 requires an NFS server supporting NFSv4.1. If your server only supports NFSv3, use nfsvers=3 instead (the nodes then also need rpcbind for file locking), or remove mountOptions to let the client and server negotiate the version.
  4. After the installation, check from an analysis node that the shared volume is writable:

    kubectl exec -n castimaging-v3 console-analysis-node-0 -- sh -c 'id; ls -ldn /opt/cast/shared; touch /opt/cast/shared/.write-test && rm /opt/cast/shared/.write-test && echo OK'
    

    The command must display uid=10001 gid=10001, a folder owned by 10001 10001, and OK.

If the volume was instead created dynamically by an NFS provisioner (for example nfs-csi, which creates one sub-folder per volume in its export), apply the same ownership to that sub-folder. Find it from the PersistentVolume bound to the claim:

kubectl get pv $(kubectl get pvc pvc-shared-datadir -n castimaging-v3 -o jsonpath='{.spec.volumeName}') -o jsonpath='{.spec.csi.volumeAttributes}'

Then, on the NFS server, run chown -R 10001:10001 and chmod 2770 on the corresponding sub-folder (for nfs-csi, <share>/<subDir>, by default <share>/<pv name>), and restart the pods using it with Util-ScaleDownAll / Util-ScaleUpAll.

The same procedure applies to the other volumes listed above if their driver does not support fsGroup: use the UID/GID of the corresponding row, and the PVC name and size (size_* values in values.yaml) of that volume in the claimRef and capacity of the PersistentVolume. For the castdir and neo4jdata volumes, which are created per pod, create one PersistentVolume per pod (for example castdir-console-analysis-node-0, castdir-console-analysis-node-1, etc.).

Use an external PostgreSQL instance

If you do not want to use the embedded PostgreSQL instance preconfigured in this helm chart, you can disable it and configure an external PostgreSQL instance instead.

  • Setup your external PostgreSQL database

    • PostgreSQL 15 - 8GB RAM minimum recommended
    • Instance must be placed in the same network and the same data center as the Kubernetes cluster nodes: this is required to avoid high network latency, which would significantly impact processing performance in CAST Imaging
    • Define a superuser authenticated by password
  • In values.yaml:

    • Set CastStorageService.enable to false (will disable the embedded PostgreSQL installation)
    • Set CustomPostgres.enable to true
    • Set CustomPostgres.host and CustomPostgres.port to match your custom database host name and port number
    • Set CustomPostgres.adminlogin and PostgresDbPassword to match the superuser login name and password of your custom database
  • Proceed with the CAST Imaging installation described above

Analysis node autoscaling

Available in ≥ 3.6.9-funcrel
The analysis node autoscaler automatically adjusts the number of console-analysis-node pods to match the analysis workload:

  • it adds analysis nodes when the job queue holds work that no running analysis node can take
  • it removes analysis nodes once they become idle, choosing which node to remove: an analysis node that is running an analysis is never removed

The autoscaler reacts to the depth of the job queue, not to CPU or memory usage. When nothing is queued, the analysis node fleet shrinks to the configured minimum. The feature is disabled by default.

How it works

The autoscaler is a small controller (console-analysis-node-autoscaler Deployment) deployed by the Helm chart when the feature is enabled. It polls the job queue exposed by console-service and resizes the console-analysis-node-core StatefulSet accordingly.

A standard Kubernetes StatefulSet always deletes the highest-numbered pod when scaling down, and that pod may be the one running an analysis. To remove a specific idle pod, the autoscaler relies on the Advanced StatefulSet provided by OpenKruise external link (reserveOrdinals feature). When autoscaling is enabled, the console-analysis-node-core StatefulSet is therefore created as an OpenKruise StatefulSet (apps.kruise.io/v1beta1) instead of a standard one (apps/v1).

Analysis capacity per node

The number of analyses an analysis node can run in parallel (its capacity) is defined by MaxParallelAnalysisPerNode in values.yaml, and can then be changed for each node in the CAST Imaging UI. The autoscaler uses both values:

  • The capacity set in the UI is the one used to compute the spare capacity of the running nodes and to decide whether a node can be removed.
  • When a node is added, a node that was previously removed is brought back first (so that its castdir volume is reused), with the capacity it had in the UI when it was removed. Previously removed nodes with the largest capacity are brought back first. Only a node that never existed starts with MaxParallelAnalysisPerNode.

To remember the capacity of removed nodes, the autoscaler records every capacity that differs from MaxParallelAnalysisPerNode in the imaging.castsoftware.com/ordinal-capacity annotation of the console-analysis-node-core StatefulSet (for example {"1": 4} for node 1 with a capacity of 4). The capacity of a removed node cannot be changed in the UI: it is restored as recorded when the node is brought back.

Requirements

  • CAST Imaging ≥ 3.6.9: earlier releases dispatch jobs directly to an analysis node, so the queue is always empty and the autoscaler never scales up.
  • OpenKruise operator installed in the cluster: this is an infrastructure prerequisite that is not installed by the CAST Imaging Helm chart (see Install OpenKruise below).
  • One schedulable worker node per analysis node: anti-affinity rules give each analysis node its own worker node, and one worker node is used by viewer-neo4j. The practical maximum number of analysis nodes is therefore schedulable worker nodes - 1.
  • Each analysis node has its own castdir volume (100Gi by default, see size_castdir). Plan block storage capacity for up to maxNodes × size_castdir.

Install OpenKruise

OpenKruise adds cluster-wide resource types (CRDs), a controller and admission webhooks, so it must be installed by a user with cluster-admin rights:

$ helm repo add openkruise https://openkruise.github.io/charts/
$ helm repo update
$ helm install kruise openkruise/kruise --version 1.9.0

Check that the OpenKruise pods are running before going further:

$ kubectl get pods -n kruise-system

See the OpenKruise installation documentation external link for more information.

Check your namespace rights (if you do not have cluster-admin rights)

If CAST Imaging is installed by a user who only has rights on the CAST Imaging namespace, the following commands must all return yes, otherwise the whole helm install / helm upgrade fails (not only the autoscaler):

$ kubectl auth can-i create statefulsets.apps.kruise.io -n castimaging-v3
$ kubectl auth can-i patch  statefulsets.apps.kruise.io -n castimaging-v3
$ kubectl auth can-i delete statefulsets.apps.kruise.io -n castimaging-v3
$ kubectl auth can-i create roles.rbac.authorization.k8s.io -n castimaging-v3
$ kubectl auth can-i create rolebindings.rbac.authorization.k8s.io -n castimaging-v3

The OpenKruise Helm chart grants its rights to the built-in admin and edit cluster roles. A RoleBinding to the built-in admin role in the namespace is therefore sufficient. The edit role is not sufficient, because it cannot create Roles and RoleBindings. If needed, ask your cluster administrator to run:

$ kubectl create rolebinding <your-user>-admin -n castimaging-v3 --clusterrole=admin --user=<your-user>

In this situation, you must also set AnalysisNodeAutoscaling.allowNodeRead: false in values.yaml (see below): with the default value (true), the chart creates a ClusterRole and a ClusterRoleBinding, which cannot be created without cluster-admin rights.

Enable the autoscaler in values.yaml

Set the following in values.yaml:

AnalysisNodeAutoscaling:
  enabled: true
  minNodes: 1
  maxNodes: 3

Then run the CAST Imaging installation (helm-install.bat|sh) or, on an existing deployment, the upgrade (helm-upgrade.bat|sh) as usual.

Settings

All settings are in the AnalysisNodeAutoscaling section of values.yaml:

Setting Default Description
enabled false Enables the autoscaler. Also switches the console-analysis-node-core StatefulSet to the OpenKruise kind.
minNodes 1 Minimum number of analysis nodes, kept even when the whole fleet is idle. Must be at least 1.
maxNodes 3 Maximum number of analysis nodes. When allowNodeRead is true, it is also capped at schedulable worker nodes - 1.
pollIntervalSeconds 30 How often the job queue is read.
scaleUpStabilizationSeconds 60 How long a scale-up decision must hold before it is applied.
scaleDownStabilizationSeconds 120 How long a scale-down decision must hold before an idle node is removed. Do not go below about 90 seconds: a single reading can catch a node between two jobs of the same application and make it look idle.
stuckPodTimeoutSeconds 600 An analysis node pod that is not Ready for this long (e.g. Pending for lack of CPU/memory, or crash-looping) is removed, and the fleet is held at the size that did start for the same duration. 0 disables this behavior.
dryRun false When true, the autoscaler computes and logs its decisions but never changes anything. Useful to observe its behavior before letting it act.
allowNodeRead true Grants a cluster-scoped read of nodes, only used to cap maxNodes at schedulable worker nodes - 1. Set to false without cluster-admin rights, on clusters with node autoscaling, or with a tainted dedicated node pool (EnforceNodeAffinity: true). maxNodes is then the only ceiling and must be sized manually.
resources requests 10m / 64Mi, limits 200m / 128Mi CPU and memory of the autoscaler pod.

Clusters with node autoscaling

When the cluster itself adds and removes worker nodes (Cluster Autoscaler on EKS, AKS or GKE, Karpenter, etc.), two autoscalers work side by side: the CAST autoscaler changes the number of analysis node pods based on the job queue, and the cluster autoscaler changes the number of worker nodes when pods are Pending for lack of room. In this case:

  • Set allowNodeRead: false. With true, the CAST autoscaler only counts the worker nodes that exist now and never requests a pod that cannot be scheduled, so the cluster autoscaler is never triggered to add a worker node.
  • Set maxNodes to the maximum size of the node pool, minus the worker node used by viewer-neo4j if it runs in the same pool.
  • Keep stuckPodTimeoutSeconds above the time needed by your cloud provider to add a worker node and pull the analysis node image. Expect a slower scale-up (several minutes) when a worker node must be added.
  • Each castdir volume is usually a zonal disk (Amazon EBS, Azure Disk LRS, Google Persistent Disk) that can only be attached in the zone where it was created. If the node pool spans several zones, use a single-zone node pool, a zone-redundant storage class for castdir (e.g. Azure ZRS, GKE regional Persistent Disk), or one node pool per zone.

Starting with 3.6.9, the console-analysis-node, console-postgres and viewer-neo4j pods carry the cluster-autoscaler.kubernetes.io/safe-to-evict: "false" and karpenter.sh/do-not-disrupt: "true" annotations (whether or not the CAST autoscaler is enabled), so a cluster autoscaler never evicts them to remove an under-used worker node and never interrupts a running analysis.

Checking that it works

$ kubectl logs -n castimaging-v3 -l imaging.service=console-analysis-node-autoscaler -f

Each poll logs what the autoscaler observed, what it decided and why, for example:

state: kind=apps.kruise.io replicas=1 ready=1 reserved=[] newNodeCapacity=2 (helm) untainted=4
queue: queued=2 nodes=[0:2/2]
decision: scale-up -> replicas=2 reserve=[]
  reason: 2 job(s) beyond 0 spare thread(s) -> +1 node(s)
  holding: decision is new, need 60s of agreement
  • newNodeCapacity is MaxParallelAnalysisPerNode as set in values.yaml: it is the capacity of a node that never existed, and is only used to compute how many nodes to add.
  • The queue: line shows the number of queued jobs and, for each running node, node number:analyses in progress/capacity, the capacity being the one set in the UI. It differs from newNodeCapacity when the capacity was changed in the UI.
  • When removed nodes have a recorded capacity (see Analysis capacity per node), it is shown at the end of the queue: line, for example reservedCapacity=[1:4].

Common log messages that are expected behavior:

Log message Meaning
holding: X/Y ready, last change has not settled A previous change is still in progress. If it persists, some analysis node pods are Pending (maxNodes too high, or not enough CPU/memory).
at the ceiling (N); M job(s) must wait maxNodes (or schedulable worker nodes - 1) has been reached. On clusters with node autoscaling, set allowNodeRead: false.
N pod(s) never became ready: ... A pod did not start within stuckPodTimeoutSeconds and has been removed. Lower maxNodes if this happens repeatedly.
nothing removable: N idle node(s), M queued job(s) need the K spare thread(s) Removing a node would leave too little capacity for queued jobs.
cannot list nodes (RBAC) - the anti-affinity ceiling is disabled Expected with allowNodeRead: false; maxNodes still applies.
refusing to scale down a native StatefulSet The StatefulSet is not the OpenKruise kind: check that OpenKruise is installed and that the upgrade was applied.

Disabling the autoscaler

Set AnalysisNodeAutoscaling.enabled: false in values.yaml, set AnalysisNodeReplicaCount to the number of analysis nodes you want, and run the upgrade. This removes the autoscaler and all the resources it owns, and restores the standard StatefulSet, which restarts the analysis nodes once (same precautions as when enabling it).

  • castdir volumes of analysis nodes numbered AnalysisNodeReplicaCount or higher are kept but no longer used. Delete them if you do not plan to re-enable autoscaling (list them with kubectl get pvc -n castimaging-v3 | grep castdir-console-analysis-node-core).
  • Always disable the autoscaler before uninstalling OpenKruise: uninstalling OpenKruise first also deletes the console-analysis-node-core StatefulSet, and the next helm upgrade fails.

Kubernetes storage location information

For information about storage locations used across the containers in a CAST Imaging Kubernetes deployment (for example persistent volumes, ConfigMap-backed configuration files, Secrets, and ephemeral (in-memory) volumes), see Kubernetes storage location information.

values.yaml parameters reference

This section describes each parameter of the values.yaml file provided at the root of the Helm chart, when and how to adjust it, and the risks of changing it on a deployment that is already running. The parameters are listed in the order in which they appear in values.yaml.

Unless stated otherwise, a change in values.yaml is applied by running helm-upgrade.bat|sh. The When can it be changed column uses the following conventions:

Label Fresh installation Existing deployment
Safe Can be set Can be changed at any time; the affected pods are restarted by the upgrade.
Caution Can be set Can be changed, but requires extra steps, causes downtime or has side effects described in the column.
Install-time only Set it before the initial installation if your environment requires it Never change it once CAST Imaging is deployed: the upgrade fails, the deployment breaks, or data is permanently lost.
Keep default Do not change it, even on a fresh installation Do not change it. These are internal values that the Helm chart and the CAST Imaging images depend on.

Sections in this reference:

Which values to review before deploying

Most parameters can keep their default values. Use the following overview to focus on the ones that matter for your installation.

1. Always review and set before the initial installation

Review these values for one or more of the following reasons:

  • Environment-specific: the default is a placeholder or a generic choice that must be adapted to your cluster (for example FrontEndHost, K8SProvider or the storage classes).
  • Security-sensitive: the default is publicly known or exposes CAST Imaging more than you may want (passwords, public load balancer).
  • Install-time only: the value can be set now, but never changed once CAST Imaging is deployed.
Parameter Why
K8SProvider Must match your cluster (EKS, AKS, GKE or K8S). Install-time only.
FrontEndHost The default (https://myhostname.com) is a placeholder: set the URL your users will use.
One Application networking option (CreateLoadBalancer, CreateIngress, UseIstioGateway, UseGatewayAPI or UseApplicationGateway) Defines how CAST Imaging is exposed to users. The default (CreateLoadBalancer: true) creates a public cloud load balancer: check that this matches your security requirements, or use UseInternalLoadBalancer or another option.
PostgresDbPassword, OperatorDbPassword + OperatorDbPasswordCrypted2, KeycloakDbPassword, Neo4jPassword, KeycloakAdminPassword (or existingSecret) The defaults are publicly known. Change them on any installation that is not a short-lived test, before the first deployment: changing them afterwards requires manual steps.
AnalysisNodeFS.* for your provider (EFSsystemID on EKS, AKSprotocol and storage account settings on AKS) Install-time only: the shared volume configuration cannot be changed after analyses have been run. EFSsystemID is mandatory on EKS. On FIPS-enabled AKS nodes, AKSprotocol: nfs is required.
CreateStorageClass, DiskClassName, FileClassName Must be false / your existing class names on-premises or when your administrator provides the storage classes. DiskClassName and FileClassName are install-time only.
RestrictedSecurityMode Set to true if your cluster enforces the restricted Pod Security Standard. Install-time only.
size_castdir, size_shared_datadir, size_db_data, size_neo4jdata Size them for the applications you plan to analyze: volumes can be extended later, but never reduced, and some require manual steps.

2. Set only if it applies to your situation

These values must be set when you use the corresponding feature or environment, and can otherwise keep their defaults:

Situation Parameters
Namespace other than castimaging-v3 namespace, and the helm-* and Util-* scripts
External PostgreSQL instead of the embedded one CastStorageService.enable, CustomPostgres.* (decide before installation)
CAST Imaging served under a context path ContextUrl.*
Ingress with automatic certificates CertManager.*
Istio or Gateway API specifics ExistingIstioGatewayName, CustomIstio*, Gateway.*
Private network only UseInternalLoadBalancer
Analysis nodes without direct internet access to CAST Extend ExtendProxy.*
License and Extend API key provided at deployment rather than in the UI ImagingLicense, ImagingLicenseType, ExtendApiKey
Private registry or mirrored images UseImagePullSecrets, Base64EncodedDockerconfigjson, ServiceAccountName, *Image.name
Passwords managed outside of values.yaml existingSecret
E-mail notifications SMTP.*
MCP server, or deployment without the Viewer McpServer.enable, Viewer.enable
Domain/tenant mapping CONTROLPANEL.IMAGING_DOMAIN_SYNCHRO_ENABLED (decide before installation)
Several analysis nodes AnalysisNodeReplicaCount, or AnalysisNodeAutoscaling.*
Large applications AnalysisNodeResources, MaxParallelAnalysisPerNode, ViewerNeo4jResources and NEO4J.* heap settings
Dedicated node pool, or specific pod distribution EnforceNodeAffinity, nodeSelector, tolerations, EnforceBalancedAffinity, BalancedAffinity.*
Remote access to the embedded PostgreSQL CreatePostgresLoadBalancer together with CastStorageService.ssl
Timeouts on long-running requests NetworkTimeoutSeconds, ExtendProxy.timeoutSeconds

3. Never change, even on a fresh installation (Keep default)

version, postgresqlDataMountPath, anadir.*, and the labelSelector* / podAffinityGroup* affinity rules.

4. Keep the defaults unless advised by CAST Support

*Image.tag and *Image.pullPolicy, the CSI driver names (*_DSdriver, *_FSdriver, install-time only), the log volume sizes, the other replica counts, the other *Resources, postgres_shmsize, AnalysisNode_shmsize, ConsoleEnableBackup, KCADMIN_USERNAME, and the ETL, AIMANAGER and GATEWAYSERVICE environment variables.

General settings

Parameter Default Description and when to adjust When can it be changed
version Current CAST Imaging release CAST Imaging release delivered by this Helm chart, used as a label (and as a fallback image tag). Do not edit: it is updated with the chart. Keep default.
namespace castimaging-v3 Kubernetes namespace in which CAST Imaging is deployed. If you need a different namespace, also replace castimaging-v3 in the helm-*.bat|sh and Util-* scripts provided with the Helm chart, which pass the namespace to helm and kubectl. Safe, provided the scripts are adjusted accordingly.
K8SProvider AKS Kubernetes provider: EKS (AWS), AKS (Azure), GKE (Google) or K8S (on-premises, with CreateStorageClass: false). Selects the CSI drivers of the storage classes, the internal load balancer annotations and some provider-specific options. Install-time only: storage classes are immutable and kept on uninstall, and existing volumes cannot move between providers.

License and CAST Extend

Parameter Default Description and when to adjust When can it be changed
ImagingLicenseType SCOPE_CD License type: SCOPE_CD (by Contributing Developers) or SCOPE_APP (by Named Application). Only used when ImagingLicense is set. Safe.
ImagingLicense empty License key. If empty, the license is entered in the Console UI after installation. Caution: when set, the value is written to the database each time console-service starts, overwriting any license entered in the UI. Keep it empty if you manage the license from the UI, or keep it up to date here.
ExtendApiKey empty CAST Extend API key. If empty, it is entered in the Console UI after installation. When set (and ExtendProxy.enable: false), the Extend URL is also set to https://extend.castsoftware.com. Caution: same behavior as ImagingLicense - re-applied at each console-service start, overwriting the value set in the UI.

Application networking

Only one of CreateIngress, UseIstioGateway, UseGatewayAPI and UseApplicationGateway can be enabled (the installation fails otherwise). CreateLoadBalancer should be set to false when one of them is used.

Parameter Default Description and when to adjust When can it be changed
FrontEndHost https://myhostname.com URL used by end users to reach CAST Imaging: scheme and host name only, no context path and no trailing /. Must match the certificate and DNS entry of your entry point. Caution: the Keycloak URL stored in the database is updated when console-service and console-control-panel restart. Update DNS and certificates at the same time, and check the Keycloak redirect URIs if you customized them. Users must log in again.
NetworkTimeoutSeconds 300 Maximum duration of a request between the entry point (Ingress, Istio or Gateway API) and console-gateway-service, after which a 504 Gateway Timeout is returned. Increase it if long-running operations time out. Safe.
CreateLoadBalancer true Exposes console-gateway-service (and extendproxy if enabled) as a cloud LoadBalancer Service. Set to false when using an Ingress, Istio, Gateway API or Azure Application Gateway. Caution: the LoadBalancer Service is kept on helm uninstall (to preserve its IP address); disabling it changes the entry point, so update DNS accordingly.
CreatePostgresLoadBalancer false Exposes the embedded PostgreSQL (console-postgres) as its own LoadBalancer Service, for example for external reporting tools. Caution: exposes the database outside the cluster. Also set CastStorageService.ssl: true, prefer UseInternalLoadBalancer: true, and restrict inbound access at network level. The Service is kept on helm uninstall.
UseInternalLoadBalancer false Requests a private (VPC/VNet only) load balancer instead of a public one for all LoadBalancer Services created by the chart. No effect with Ingress, Istio, Gateway API or Application Gateway. Caution: the load balancer is re-created with a new IP address; update DNS. Clients outside the private network lose access.
CreateIngress false Routes traffic through an NGINX Ingress controller (installed separately). Caution: changes the entry point (new IP address/DNS).
CertManager.enable false Only with CreateIngress: true. Adds cert-manager annotations so that the TLS certificates (tls-secret-cast, and tls-secret-cast-extend with ExtendProxy) are requested and renewed automatically. cert-manager and the Issuer must already exist. Safe.
CertManager.issuer vault-issuer Name of the cert-manager Issuer or ClusterIssuer. Safe.
CertManager.issuerKind Issuer Issuer (in the CAST Imaging namespace) or ClusterIssuer. Safe.
CertManager.privateKeySize "4096" Private key size in bits. Safe (applies to the next certificate issued).
UseIstioGateway false Routes traffic through an Istio Gateway and VirtualServices (Istio installed separately). Caution: changes the entry point.
ExistingIstioGatewayName "" Name of an existing Istio Gateway to attach to (e.g. aks-istio-ingress/gateway-wildcard) instead of creating one. Caution: changes the entry point.
CustomIstioSelector empty Optional value of the istio label selecting the Istio ingress gateway pods (default: ingressgateway). Caution: changes the entry point.
CustomIstioMainCredentialName empty Optional name of the TLS Secret used by the Istio Gateway for FrontEndHost (default: tls-secret-cast). Safe, if the Secret exists.
CustomIstioExtendCredentialName empty Optional name of the TLS Secret used for the ExtendProxy host name (default: tls-secret-cast-extend). Safe, if the Secret exists.
UseGatewayAPI false Routes traffic through a Kubernetes Gateway API Gateway and HTTPRoutes (a GatewayClass controller such as NGINX Gateway Fabric must be installed). Caution: changes the entry point.
Gateway.create true true creates a dedicated Gateway; false attaches the HTTPRoutes to an existing shared Gateway (Gateway.name / Gateway.namespace). Caution: changes the entry point.
Gateway.name / Gateway.namespace castimaging-gateway / "" Gateway to attach to when Gateway.create: false. Caution.
Gateway.className nginx GatewayClass of the dedicated Gateway (nginx for NGINX Gateway Fabric). Caution: the Gateway is re-created.
UseApplicationGateway false AKS only: set to true when an Azure Application Gateway (created separately) is used in front of CAST Imaging. Caution: changes the entry point.
ContextUrl.enable false Serves CAST Imaging under a context path (e.g. https://myhostname.com/cast). Requires CreateIngress, UseIstioGateway or UseGatewayAPI. Caution: after helm upgrade, restart all CAST Imaging services once (Util-ScaleDownAll then Util-ScaleUpAll). On a first installation with enable: true, also restart all services once after the initial startup.
ContextUrl.appcontext /mycontext Context path, starting with / and without trailing /. Caution: same as ContextUrl.enable.

Storage

See Kubernetes storage location information for the list of volumes.

Parameter Default Description and when to adjust When can it be changed
CreateStorageClass true Creates the block (DiskClassName) and file (FileClassName) storage classes. Set to false if your cluster administrator provides them (mandatory on-premises); DiskClassName and FileClassName must then match the existing class names. Safe: the storage classes are kept on uninstall and are not deleted when switching to false.
DiskClassName castimaging-ds Storage class used for all block (ReadWriteOnce) volumes. Install-time only: the storage class of an existing PVC or StatefulSet volume template is immutable, so the upgrade fails. Re-creating the volumes causes permanent data loss.
FileClassName castimaging-fs Storage class used for the shared pvc-shared-datadir volume, which must support ReadWriteMany, even with a single analysis node: the volume is also mounted by console-service and console-control-panel. Install-time only: on an instance that has run analyses, it requires deleting pvc-shared-datadir, causing permanent data loss.
AnalysisNodeFS.EFSsystemID fs-xxxxxxxxxxx EKS only, required: ID of the Amazon EFS file system used for pvc-shared-datadir. Install-time only: storage class parameters are immutable and the shared data lives on this file system.
AnalysisNodeFS.EFSaccessPointID empty EKS only, optional: when set, an explicit PersistentVolume bound to this EFS access point is created instead of a dynamically provisioned one. Install-time only.
AnalysisNodeFS.AKSresourceGroup empty AKS only, optional: resource group of the storage account used for Azure Files. Install-time only: storage class parameters are immutable.
AnalysisNodeFS.AKSstorageAccount empty AKS only, optional: existing storage account used for Azure Files (otherwise created automatically by the CSI driver). Install-time only.
AnalysisNodeFS.AKSprotocol smb AKS only: smb or nfs. nfs is required on FIPS-enabled nodes and needs a Premium FileStorage account reachable on TCP 2049. Install-time only: on an instance that has run analyses, switching requires deleting the storage class and pvc-shared-datadir, causing permanent data loss.
AnalysisNodeFS.AKSnfsEncryptInTransit true AKS with AKSprotocol: nfs only: encrypts NFS traffic with TLS, which allows “Secure transfer required” to stay enabled on the storage account. Set to false only for plain-text NFS. Install-time only: storage class parameter, immutable.
AnalysisNodeFS.AKSsharedDatadirPV.create false AKS only: set to true when dynamic provisioning cannot be used, to create an explicit PersistentVolume on an existing Azure File Share (SMB only). Install-time only.
AnalysisNodeFS.AKSsharedDatadirPV.secretName / shareName cast-fs-secret / cast-fileshare Secret holding the storage account credentials and name of the Azure File Share, required when AKSsharedDatadirPV.create: true. Install-time only.
EKS_DSdriver, EKS_FSdriver, AKS_DSdriver, AKS_FSdriver, GKE_DSdriver, GKE_FSdriver CSI drivers of each provider CSI provisioners used by the storage classes. Only change them if your cluster uses a different CSI driver. Install-time only: storage classes are immutable.

Volume sizes

Parameter Default Volume
size_castdir 100Gi castdir of each analysis node (StatefulSet volume template)
size_shared_datadir 100Gi pvc-shared-datadir (shared analysis data)
size_db_data 128Gi db-data (embedded PostgreSQL)
size_neo4jdata 100Gi neo4jdata of viewer-neo4j (StatefulSet volume template)
size_imagingviewer_v3_server_log 1Gi viewer-server logs
size_imagingviewer_v3_etl_logs 10Gi viewer-etl logs
size_imagingviewer_v3_etl_csvarchive 10Gi viewer-etl CSV archive
size_imagingviewer_v3_aimanager_logs 2Gi viewer-aimanager logs
size_imagingviewer_v3_api_logs 2Gi viewer-api logs
size_extendproxy_data 10Gi extendproxy data
size_mcpserver_logs 2Gi mcp-server logs
size_mcpserver_data 1Gi mcp-server data: Text2Cypher SQLite stores (≥ 3.6.9)
size_consoleservice_logs, size_controlpanel_logs, size_gatewayservice_logs, size_authenticationservice_logs, size_ssoservice_logs, size_dashboards_logs 2Gi each Console services logs

Size the volumes according to the number and size of the applications you plan to analyze: size_castdir, size_shared_datadir, size_db_data and size_neo4jdata are the ones that grow with usage.

PostgreSQL

Enable either CastStorageService (embedded PostgreSQL) or CustomPostgres (external PostgreSQL), not both.

Parameter Default Description and when to adjust When can it be changed
CastStorageService.enable true Deploys the embedded PostgreSQL (console-postgres StatefulSet and db-data volume). Set to false to use an external PostgreSQL instance. Install-time only: switching between embedded and external PostgreSQL does not migrate any data, and the deployment restarts on an empty database. A migration requires a backup/restore procedure.
CastStorageService.ssl false Enforces SSL connections to the embedded PostgreSQL, with a self-signed certificate generated at startup. Recommended when CreatePostgresLoadBalancer: true. Caution: restarts PostgreSQL and therefore interrupts all services; do it when no analysis is running.
CustomPostgres.enable false Uses an existing external PostgreSQL server (PostgreSQL 15, 8GB RAM minimum recommended). The required users and databases are created by a one-time Job. Install-time only: see CastStorageService.enable.
CustomPostgres.host mypostgreshostname Host name of the external PostgreSQL server. Caution: the new server must contain the CAST Imaging data (restored from a backup), otherwise CAST Imaging starts on an empty database.
CustomPostgres.adminlogin postgres Superuser login of the external server; its password is set in PostgresDbPassword. Safe, if the login is valid.
CustomPostgres.port 2285 Port of the external PostgreSQL server. Safe, if the port is valid.
postgres_shmsize 4G Shared memory (/dev/shm) of the embedded PostgreSQL, used for parallel queries. Increase only if PostgreSQL reports shared memory errors. Caution: restarts PostgreSQL.
postgresqlDataMountPath /var/lib/postgresql/data Data directory of the embedded PostgreSQL, matching the PostgreSQL image provided with the chart. Keep default: on an existing deployment, PostgreSQL would start on an empty data directory.

Passwords and Secrets

All passwords are base64-encoded: echo -n "mypassword" | base64 on Linux, or [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("mypassword")) in PowerShell.

Parameter Default Description and when to adjust When can it be changed
existingSecret "" Name of an existing Kubernetes Secret holding the passwords, for example when passwords are managed by an external secret store (see Using an external secret store). When set, all password values below are ignored, and the Secret must contain the keys postgres-db-password, operator-db-password, operator-db-password-crypted2, keycloak-db-password, keycloak-admin-password, neo4j-password and optionally smtp-password. Otherwise, the chart creates the imaging-pwd-sec Secret. Caution: the new Secret must contain the passwords currently in use, otherwise services can no longer connect to the databases.
PostgresDbPassword postgres (encoded) Password of the postgres superuser of the embedded PostgreSQL, or of CustomPostgres.adminlogin on an external server. Caution: changing the value alone breaks the connection. First change the password in PostgreSQL (ALTER USER ... WITH PASSWORD '...') while CAST Imaging is running, then update the value and run helm upgrade.
OperatorDbPassword CastAIP (encoded) Password of the operator database user used by all Console services. Caution: change it first in PostgreSQL, then in the Console (Global configuration > CSS and Measurement settings), then update both OperatorDbPassword and OperatorDbPasswordCrypted2 and run helm upgrade.
OperatorDbPasswordCrypted2 CRYPTED2:... operator password encrypted with the cast-imaging-encryption-tool.jar tool (provided with the CAST Imaging Linux installer). Not base64-encoded. Must always match OperatorDbPassword. Caution: see OperatorDbPassword.
KeycloakDbPassword keycloak (encoded) Password of the keycloak database user. Caution: change it first in PostgreSQL, then update the value and run helm upgrade.
Neo4jPassword imaging (encoded) Password of the Neo4j database used by the Viewer services. Caution: the Neo4j password is stored in the Neo4j data volume. Changing only the value breaks the Viewer; it must also be changed in Neo4j.
KeycloakAdminPassword admin (encoded) Password of the Keycloak admin service account, used for communication between components. Change it on fresh installations. Caution: change it first in the Keycloak admin UI while CAST Imaging is running, then update the value, run Util-ScaleDownAll and helm-upgrade. See Keycloak accounts and passwords.
KCADMIN_USERNAME kcadmin Login of the Keycloak administrator account created at installation, used to access the Keycloak UI. Caution: the Keycloak initialization Job runs again and creates a new administrator with the new name; the previous account is not deleted.

Using an external secret store

existingSecret accepts any Kubernetes Secret, whatever tool creates it. To keep the passwords in an external secret store, use a tool that creates the Secret in the CAST Imaging namespace from that store. The most common one is External Secrets Operator external link (ESO), which works the same way on every platform and supports, among others:

Platform Typical secret store Authentication of ESO to the store
EKS AWS Secrets Manager or AWS Systems Manager Parameter Store IAM Roles for Service Accounts (IRSA) or EKS Pod Identity
AKS Azure Key Vault Microsoft Entra Workload ID
GKE Google Cloud Secret Manager Workload Identity Federation for GKE
On-premises HashiCorp Vault, CyberArk Conjur, Akeyless, or the store already used in your organization Kubernetes auth method, AppRole or token, depending on the store

Vendor operators (for example HashiCorp Vault Secrets Operator) and GitOps tools that decrypt secrets in the cluster (Sealed Secrets, SOPS) also work, as long as the result is a Secret with the expected keys. The Secrets Store CSI Driver is not suitable: it only synchronizes a Secret while a pod mounts the corresponding CSI volume, and CAST Imaging pods do not.

To use ESO:

  1. Install ESO and create a SecretStore (in the CAST Imaging namespace) or a ClusterSecretStore for your store. See the list of ESO providers external link for the configuration of your store.

  2. In your secret store, create a secret (for example cast-imaging/passwords) containing a JSON object with the keys postgres-db-password, operator-db-password, operator-db-password-crypted2, keycloak-db-password, keycloak-admin-password, neo4j-password and, when SMTP.enable is true, smtp-password. Store the values in plain text, not base64-encoded. For operator-db-password-crypted2, store the complete CRYPTED2:... string generated by cast-imaging-encryption-tool.jar.

  3. Create the namespace (kubectl create namespace castimaging-v3) and an ExternalSecret in it:

    apiVersion: external-secrets.io/v1
    kind: ExternalSecret
    metadata:
      name: imaging-passwords
      namespace: castimaging-v3
    spec:
      refreshInterval: 1h
      secretStoreRef:
        kind: ClusterSecretStore      # or SecretStore
        name: my-secret-store
      target:
        name: imaging-passwords       # Secret name to set in existingSecret
      dataFrom:
        - extract:
            key: cast-imaging/passwords
    

    On ESO versions earlier than 0.17, use apiVersion: external-secrets.io/v1beta1. If your store holds one secret per password instead of a JSON object, replace dataFrom with one data entry per key (secretKey set to the expected key name, remoteRef.key set to the name in your store).

  4. Check that the Secret is synchronized: kubectl get externalsecret imaging-passwords -n castimaging-v3 must show SecretSynced.

  5. Set existingSecret: imaging-passwords in values.yaml and run the installation (or helm-upgrade on an existing deployment, with the passwords currently in use).

Optional components

Parameter Default Description and when to adjust When can it be changed
ExtendProxy.enable false Deploys the CAST Extend Proxy (extendproxy), used when the analysis nodes cannot reach extend.castsoftware.com directly. Safe to enable. When disabled, its data volume is kept.
ExtendProxy.exthostname myextendhost.com Host name used to reach the Extend Proxy. Same host name as FrontEndHost: routed under /extendproxy. Different host name: routed on a dedicated host name and certificate. Caution: update DNS and certificates; clients configured with the previous URL must be updated.
ExtendProxy.timeoutSeconds 3600 Same as NetworkTimeoutSeconds for the Extend Proxy path (bundle uploads can be large). Safe.
Viewer.enable true Deploys the Viewer components (viewer-neo4j, viewer-server, viewer-etl, viewer-api, viewer-aimanager) and mcp-server. Set to false for a deployment without the Viewer. Caution: disabling removes the Viewer services; their volumes, including the Neo4j data, are kept and reused if re-enabled.
McpServer.enable false Deploys the MCP server (ignored when Viewer.enable: false). Starting with 3.6.9 (MCP Server 3.1), the mcp-server pod runs the MCP Aggregator Gateway (port 8285, registered in the Control Panel and reached by MCP clients at <FrontEndHost>/mcp) with the Structural, Semantic search and Text2Cypher MCP servers behind it, and connects to Neo4j (viewer-neo4j). GraphRAG (Semantic search) requires a specific CAST Imaging license. Its settings are in the MCPSERVER section (see Extra environment variables). Safe. When disabled, its volumes are kept.
SMTP.enable false Enables e-mail notifications from the Control Panel. Safe.
SMTP.SPRING_MAIL_HOST, SPRING_MAIL_PORT, SPRING_MAIL_USERNAME, SPRING_MAIL_PROTOCOL example values SMTP server settings. Safe.
SMTP.SPRING_MAIL_PASSWORD encoded example SMTP password, base64-encoded. Safe.
SMTP.SPRING_MAIL_PROPERTIES_MAIL_SMTP_AUTH, SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE true SMTP authentication and STARTTLS. Safe.

Extra environment variables

The sections NEO4J, ETL, SERVER, AIMANAGER, MCPSERVER, CONTROLPANEL, ANALYSISNODE, API, AUTHENTICATIONSERVICE, CONSOLESERVICE, DASHBOARDS, GATEWAYSERVICE, SSOSERVICE and EXTENDPROXY add environment variables to the corresponding containers (NAME: value). Only add variables documented by CAST or requested by CAST Support. Changing them restarts the corresponding pods.

Parameter Default Description and when to adjust
NEO4J.NEO4J_server_memory_heap_initial__size / NEO4J_server_memory_heap_max__size 12G Neo4j Java heap. Increase for large applications, keeping it well below the ViewerNeo4jResources memory limit.
NEO4J.NEO4J_dbms_memory_transaction_total_max 10G Maximum memory used by Neo4j transactions.
ETL.WORKER_POOL_SIZE 4 Number of parallel ETL workers when importing analysis results into Neo4j.
ETL.NEO4J_IMPORT_BATCH_SIZE / NEO4J_DELETE_BATCH_SIZE 1000 / 2000 Batch sizes used by the ETL when writing to and deleting from Neo4j. Keep the defaults unless advised by CAST Support.
AIMANAGER.SUMMARY_LOG_LEVEL info Log level of the AI Manager.
MCPSERVER.STRUCTURAL_MCP_ENABLED, SEMANTIC_MCP_ENABLED, TEXT_TO_CYPHER_MCP_ENABLED true MCP servers run behind the MCP Aggregator Gateway (≥ 3.6.9). Semantic search (the GraphRAG MCP tools) requires the Neo4j embeddings to be configured in the CAST Imaging AI settings, and a specific CAST Imaging license to enable GraphRAG: contact CAST to obtain it.
MCPSERVER.STRUCTURAL_MCP_TOOL_SURFACE_PROFILE, SEMANTIC_MCP_TOOL_SURFACE_PROFILE intents Tools exposed to MCP clients: intents (meta-tools) or full (every tool) (≥ 3.6.9).
MCPSERVER.IMAGING_CODE "False" Set to "True" to let MCP clients read the application source code. Security sensitive: enable it only if the MCP clients and their users are allowed to access the source code.
MCPSERVER.LOG_LEVEL INFO Log level of the MCP server: INFO or DEBUG only (any other value stops the container) (≥ 3.6.9).
MCPSERVER.TELEMETRY_ENABLED, TELEMETRY_PUSH_ENABLED true MCP usage telemetry, and its periodic push to CAST (every TELEMETRY_PUSH_INTERVAL_MINUTES, default 120) (≥ 3.6.9). Set TELEMETRY_PUSH_ENABLED: false when the cluster has no outbound internet access.
CONTROLPANEL.IMAGING_DOMAIN_SYNCHRO_ENABLED false Enables domain/tenant mapping. Decide it at installation time if you need it: it is not straightforward to enable after installation.
GATEWAYSERVICE.SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_TRUSTEDPROXIES ".*" Regular expression of the proxies trusted by console-gateway-service to forward the X-Forwarded-* headers; required for correct login redirects behind an Ingress, Istio or Gateway API. Can be restricted to the IP range of your gateway pods (e.g. "10\..*"). Do not remove it.

Analysis settings

Parameter Default Description and when to adjust When can it be changed
MaxParallelAnalysisPerNode 2 Maximum number of analyses run in parallel by each analysis node. Increase only if AnalysisNodeResources (CPU and memory) are increased accordingly. Caution: restarts the analysis nodes; do it when no analysis is running.
ConsoleEnableBackup true Takes a backup of each application before it is upgraded. Disabling it saves time and disk space, but no backup is then available if an application upgrade fails. Caution: restarts the analysis nodes.
AnalysisNode_shmsize 1G Shared memory (/dev/shm) of the analysis nodes. Caution: restarts the analysis nodes.
anadir.* /opt/cast/shared/..., /usr/share/CAST/... Folders used by the analysis nodes in their volumes. Keep default: the analysis node image expects these paths, and analysis data would no longer be found.

Security and registry access

Parameter Default Description and when to adjust When can it be changed
RestrictedSecurityMode false Set to true when the cluster enforces the Kubernetes “restricted” Pod Security Standard (or an equivalent policy): non-root users, read-only root file systems, dropped capabilities, seccomp profile. Install-time only: cannot be changed after CAST Imaging is deployed.
UseImagePullSecrets false Set to true to pull images from a private registry with the credentials provided in Base64EncodedDockerconfigjson (creates the imagepull-sec Secret). Safe.
Base64EncodedDockerconfigjson empty Base64-encoded content of a Docker config.json file with the registry credentials. Safe.
ServiceAccountName empty Existing Kubernetes ServiceAccount used by the CAST Imaging pods, for example one with its own imagePullSecrets or cloud identity. Caution: all pods restart.

Replica counts

Parameter Default Description and when to adjust
AnalysisNodeReplicaCount 1 Number of analysis nodes. Increase to run more analyses in parallel; each analysis node requires its own cluster node when EnforceBalancedAffinity: true. Ignored when AnalysisNodeAutoscaling.enabled: true. Reducing it removes the analysis nodes with the highest numbers, even if they are running an analysis: reduce it only when no analysis is running.
ConsolePostgresReplicaCount, ViewerNeo4jReplicaCount 1 Single-instance databases: must be 1 (or 0 to stop them). Never set more than 1.
SSOServiceReplicaCount, ConsoleServiceReplicaCount, GatewayServiceReplicaCount, ControlPaneReplicaCount, AuthenticationServiceReplicaCount, DashboardReplicaCount, ExtendProxyReplicaCount, ViewerServerReplicaCount, ViewerEtlReplicaCount, ViewerAiManagerReplicaCount, ViewerApiReplicaCount, McpServerReplicaCount 1 Must be 1. 0 is used to deploy without starting the services (for example with EnforceNodeAffinity, see below).

To stop and start CAST Imaging, use the Util-ScaleDownAll and Util-ScaleUpAll scripts rather than changing the replica counts.

Analysis node autoscaling

The AnalysisNodeAutoscaling parameters (enabled, minNodes, maxNodes, pollIntervalSeconds, scaleUpStabilizationSeconds, scaleDownStabilizationSeconds, stuckPodTimeoutSeconds, dryRun, allowNodeRead, resources) are described in the Analysis node autoscaling section of this page.

Images

Each *Image parameter (InitUtilImage, ConsolePostgresImage, ConsoleSSOServiceImage, ConsoleServiceImage, ConsoleGatewayServiceImage, ConsoleAnalysisNodeImage, ConsoleControlPaneImage, ConsoleAuthenticationServiceImage, ConsoleIntegratedDashboardsImage, ViewerNeo4jImage, ViewerServiceImage, ViewerEtlImage, ViewerAiManagerImage, ViewerApiImage, McpServerImage, ExtendProxyImage) has three settings:

Setting Description and when to adjust
name Image repository. Change it only to pull the images from your own registry (mirror of Docker Hub), keeping the same image path.
tag Image version. The tags are validated together for a given release: do not change them individually. To upgrade CAST Imaging, use the values.yaml of the new release branch instead.
pullPolicy Always or IfNotPresent. Keep the defaults.

Resources

The *Resources parameters (ConsolePostgresResources, SSOServiceResources, ConsoleServiceResources, GatewayServiceResources, AnalysisNodeResources, ControlPanelResources, AuthenticationServiceResources, IntegratedDashboardsResources, ViewerNeo4jResources, ViewerServerServiceResources, ViewerEtlResources, ViewerAiManagerResources, ViewerApiResources, McpServerResources, ExtendProxyResources, InitContainerResources) define the CPU and memory requests (reserved for the pod) and limits (maximum allowed) of each container.

  • Increase AnalysisNodeResources when analyzing large applications or when increasing MaxParallelAnalysisPerNode, and ViewerNeo4jResources (together with the NEO4J heap settings) for large applications in the Viewer.
  • The cluster nodes must have enough allocatable CPU and memory for the requests, otherwise pods stay Pending.
  • A container exceeding its memory limit is restarted (OOMKilled).

On an existing deployment, changing resources restarts the corresponding pods: for AnalysisNodeResources, ConsolePostgresResources and ViewerNeo4jResources, do it when no analysis is running.

Pod placement

Parameter Default Description and when to adjust When can it be changed
EnforceBalancedAffinity true Spreads pods over the cluster nodes: viewer-neo4j and analysis nodes never share a node, and two analysis nodes never share a node. Requires at least 2 usable cluster nodes, plus one per additional analysis node. Cannot be combined with EnforceNodeAffinity. Caution: pods may move to other nodes. With zonal block storage, a pod cannot move to a node in another zone than its volume and stays Pending.
BalancedAffinity.EnforceAnalysisNodeIsolation false Runs each analysis node on a dedicated cluster node (no other CAST Imaging pod). Requires additional cluster nodes. Caution: same as EnforceBalancedAffinity.
BalancedAffinity.EnforceNeo4jIsolation false Runs viewer-neo4j on a dedicated cluster node. Caution.
BalancedAffinity.EnforcePostgresIsolation false Runs the embedded PostgreSQL on a dedicated cluster node. Caution.
EnforceNodeAffinity false Runs CAST Imaging on a dedicated node pool (XL deployments, 4 nodes minimum: PostgreSQL, Console services, Viewer services, one per analysis node). The nodes must have the label app: castimaging and the taint workload=castimaging:NoSchedule. Deploy initially with all replica counts at 0, then run Util-ScaleUpAllWithNodeAffinities. Cannot be combined with EnforceBalancedAffinity. Caution: all pods move to the dedicated node pool. Block volumes cannot move between zones: the dedicated nodes must be in the same zones as the existing volumes.
nodeSelector, tolerations, nodeSelectorAppcastimaging app: castimaging, workload=castimaging:NoSchedule Label and taint of the dedicated node pool used with EnforceNodeAffinity. Change them only if your node pool uses other labels or taints. Caution: same as EnforceNodeAffinity.
labelSelector*, podAffinityGroup* - Affinity rules used internally by EnforceBalancedAffinity and EnforceNodeAffinity. Keep default.