Kubernetes
Kasm Workspaces core services can be deployed to Kubernetes using the open-source Kasm Helm chart, which will be Generally Available as of Kasm version 1.19.0 (the developer preview is available using chart version 1.1190.0-develop).
Not sure which components you need? The Helm chart does not cover everything — what you provision alongside it depends on your session types (Docker containers, RDP, VNC) and whether you are running multiple zones. See Kubernetes Deployment Options for a full breakdown before you start.
Quick Start
If you have a running Kubernetes cluster and just want to get Kasm up quickly, here are the minimal steps. See Installation below for the full walkthrough with all options.
Prerequisites: Kubernetes 1.24+, Helm 3.18.x+, a domain name, and a TLS certificate or cert-manager.
Create a namespace and install your TLS certificate as a Kubernetes secret (see Configure TLS Certificates for the available methods), then create a minimal values file:
# my-values.yaml
publicAddr: kasm.example.com
certificate:
secretName: {CERTIFICATE_SECRET_NAME}
Install the chart:
kubectl create namespace {NAMESPACE}
helm install {RELEASE_NAME} oci://registry-1.docker.io/kasmweb/kasm-helm \
--version 1.1190.0-develop -n {NAMESPACE} -f my-values.yaml
Wait for pods to be ready, then retrieve the admin password:
kubectl get pods -n {NAMESPACE} --watch
kubectl get secret --namespace {NAMESPACE} {RELEASE_NAME}-secrets \
-o jsonpath="{.data.admin-password}" | base64 -d; echo
Point your DNS for publicAddr to the address shown by kubectl get ingress -n {NAMESPACE} and log in at https://{publicAddr} with admin@kasm.local.
After a successful install, disable the one-time initialization jobs before any future helm upgrade:
dbManagement:
initialize: false
upgrade:
enable: false
Architecture Overview
The Kasm Helm chart deploys the core control-plane services inside a Kubernetes cluster. Containerized desktop and application sessions are not run inside the cluster; they are hosted on external Docker Agent servers that you provision separately (either as static agents or via auto scaling). RDP sessions are routed to external Windows or Linux hosts running RDP servers.
The following diagram shows the main components and how traffic flows between them:
Multi-Zone Architecture
Kasm supports multiple deployment zones, which are commonly used to serve users across geographic regions but can also be used to segment groups of users or isolate different workload types across separate pools of compute resources. In a multi-zone deployment, each zone runs its own set of API-layer and proxy-layer services. Additional zones only receive the App role from the Helm chart — their proxy layer is deployed separately using the multi-zone proxy instructions.
Key points to note:
- Agents are not included in the Helm chart. The Kasm Agent, which hosts containerized sessions (desktops, browsers, apps), must be deployed on separate Docker hosts outside the cluster. You can add agents manually as static servers or configure auto scaling to provision them on demand.
- RDP over port 3389 requires extra configuration. Standard Kubernetes Ingress only handles HTTP/HTTPS traffic on port 443. By default, thick-client RDP connections route through the RDP HTTPS Gateway on port 443, which works out of the box. To support direct RDP connections on port 3389, you must expose the RDP Gateway via a NodePort or LoadBalancer service. See Direct RDP Connections on Kubernetes for setup instructions.
- Ingress acts as the external load balancer. All browser and HTTPS-based client traffic enters the cluster through the Ingress controller, which should be backed by a cloud load balancer or similar in production.
Prerequisites
Before installing, ensure you have:
- Kubernetes 1.24+
- Helm 3.18.x+: install from helm.sh
kubectlconfigured and connected to your target cluster- A default StorageClass (or explicit
storageClassNamein your values): required for the built-in PostgreSQL database PVC. Not needed if using a standalone external database. Runkubectl get storageclassto verify a default is set. - A domain name with DNS control, to use as the
publicAddrfor Kasm - A TLS certificate or cert-manager installed in your cluster. See Configure TLS Certificates for Kasm on Kubernetes
Note that Helm chart versions are based upon Kasm versions, for example chart version 1.1190.0-develop corresponds to Kasm version 1.19.0, with the minor (middle) number corresponding to the officially supported Kasm version.
If you are deploying on a managed Kubernetes platform such as Amazon EKS, Google GKE, or Azure AKS, ensure that your cloud provider's CLI tools are installed and configured (e.g., aws, gcloud, az) and that your kubectl context is pointed at the correct cluster. Refer to your provider's documentation for cluster setup and authentication:
Cloud-hosted deployments may also benefit from using a managed database service (e.g., Amazon RDS, Cloud SQL, Azure Database for PostgreSQL) instead of the built-in database. See the chart's database values for external database configuration.
Installation
If you are using a developer preview version of the Kasm Helm chart, be aware that no clean migration path to future updates is guaranteed. Review the Developer Preview Builds warnings before proceeding.
If your cluster cannot reach public registries, see Offline Installations at the bottom of this page for the additional image-prep and values-file overrides you will need.
0. Verify Your Cluster
Before proceeding, confirm that your cluster is reachable and that nodes are healthy:
kubectl cluster-info
kubectl get nodes
All nodes should show a Ready status. If not, resolve any cluster issues before continuing.
1. Create the Namespace
kubectl create namespace {NAMESPACE}
2. Configure TLS
Install your TLS certificate into the namespace as a Kubernetes secret. See Configure TLS Certificates for Kasm on Kubernetes for the available methods.
Note the secret name you create, as you will need it as {CERTIFICATE_SECRET_NAME} in the next step.
3. Create Your Values File
Create a my-values.yaml file. At minimum, include:
publicAddr: kasm.example.com
certificate:
secretName: {CERTIFICATE_SECRET_NAME}
Refer to the chart definition for a list of all available configuration options (documented in /charts/kasm/values.yaml).
Customizing Component Image Tags
By default, each component uses a default tag which can be overridden using the key useImageTags. Components can deviate using components.*.image.tag, so the priority order is "component-specific", then "useImageTags", then the default for the chart (i.e., develop):
useImageTags: develop
components:
proxy:
image:
tag: some-custom-sha:256
api:
image:
tag: some-custom-sha:256
# manager:
# image:
# tag: some-custom-sha:256
# guac:
# image:
# tag: some-custom-sha:256
# rdpGateway:
# image:
# tag: some-custom-sha:256
# rdpHttpsGateway:
# image:
# tag: some-custom-sha:256
database:
image:
tag: some-custom-sha:256
You can override any subset of components independently; omitted components continue to use the chart's default tag.
To apply tag changes to a running deployment after initial install, see Applying Changes to a Running Instance below.
deploymentSize Variable ValuedeploymentSize controls resource allocation across all Kasm components.
Three values are supported: small, medium, and large. Each value maps to a preset that sets CPU and memory requests/limits and scales the replica count for each component (1/2/3 respectively). The database StatefulSet always runs as a single replica regardless of size.
Choose small for evaluation, single-node, or low-concurrency deployments. Use medium or large for production environments where session throughput and availability matter. Any component's resources or replica count can be overridden individually via components.<name>.resources and components.<name>.replicas when the preset does not match your workload.
4. (Optional) Pre-Create Kasm Secrets
By default, the Helm chart auto-generates random credentials during installation and stores them in a Kubernetes secret. If you prefer to use your own credentials, create the secret before installing the chart.
The secret must be named {RELEASE_NAME}-secrets, where {RELEASE_NAME} is the name you will pass to helm install in the next step. For example, if you plan to run helm install kasm ..., the secret must be named kasm-secrets.
apiVersion: v1
kind: Secret
metadata:
name: {RELEASE_NAME}-secrets
namespace: {NAMESPACE}
type: Opaque
data:
admin-password: "<base64-encoded value>"
user-password: "<base64-encoded value>"
db-password: "<base64-encoded value>"
manager-token: "<base64-encoded value>"
service-token: "<base64-encoded value>"
Apply it with:
kubectl apply -f kasm-secrets.yaml
To base64-encode a value: echo -n 'my-password' | base64
If the chart finds an existing secret with this name during installation, it will use those values instead of generating new ones.
5. Install the Chart
The Kasm Helm chart is available as an OCI Helm chart and from a classic Helm repository.
OCI Registry (Recommended)
helm install {RELEASE_NAME} oci://registry-1.docker.io/kasmweb/kasm-helm \
--version 1.1190.0-develop -n {NAMESPACE} -f my-values.yaml
Classic Helm Repository
helm repo add kasm https://helm.kasm.com
helm repo update
helm install {RELEASE_NAME} kasm/kasm-helm --version 1.1190.0-develop -n {NAMESPACE} -f my-values.yaml
Once the installation completes, disable the one-time initialization jobs in your values file before running any future helm upgrade. Leaving them enabled will cause them to re-run on every upgrade:
dbManagement:
initialize: false
upgrade:
enable: false
To customize the Helm chart beyond what values allow, clone the kasm-helm GitHub repository and check out the branch corresponding to your target Kasm version (e.g., develop):
git clone --branch develop https://github.com/kasmtech/kasm-helm.git
cd kasm-helm
Then install from the local chart directory:
helm install {RELEASE_NAME} ./charts/kasm-helm -n {NAMESPACE} -f my-values.yaml
6. Verify All Pods Are Running
kubectl get pods -n {NAMESPACE} --watch
Wait until all pods show Running or Completed, then press Ctrl+C.
Post-Installation
Configure DNS
Retrieve the ingress address assigned to your deployment:
kubectl get ingress -n {NAMESPACE}
Update your DNS record for publicAddr to point to the value in the ADDRESS column. Once DNS has propagated, Kasm will be reachable at https://{publicAddr}.
Retrieve Credentials
The quickest way to see all post-install information is:
helm get notes {RELEASE_NAME} -n {NAMESPACE}
Or retrieve credentials directly:
# Admin password (login: admin@kasm.local)
kubectl get secret --namespace {NAMESPACE} {RELEASE_NAME}-secrets \
-o jsonpath="{.data.admin-password}" | base64 -d; echo
# User password (login: user@kasm.local)
kubectl get secret --namespace {NAMESPACE} {RELEASE_NAME}-secrets \
-o jsonpath="{.data.user-password}" | base64 -d; echo
Pulling the Latest Build
The chart uses rolling image tags which are rebuilt with the latest software updates and patches on a regular schedule. While Kubernetes does not automatically check for newer images for a running service, you can trigger updates by restarting each service and ensuring the imagePullPolicy is set to Always (which is the chart's default).
If you need to pin a specific build, you can pin to a timestamped tag such as develop-rolling-20260215 following the section Customizing Component Image Tags. Timestamped tags are immutable so a rollout restart will not pull a newer build. To advance to a newer timestamped build, update the tag in your values file and run helm upgrade.
Use the command kubectl rollout restart to restart services and pull updates. This type of restart will respect the uptime policies in the chart and minimize disruptions.
kubectl rollout restart deployment/{RELEASE_NAME}-api \
deployment/{RELEASE_NAME}-manager \
deployment/{RELEASE_NAME}-proxy \
deployment/{RELEASE_NAME}-guac \
deployment/{RELEASE_NAME}-rdp-gateway \
deployment/{RELEASE_NAME}-rdp-https-gateway \
-n {NAMESPACE}
Monitor rollout progress for any individual deployment:
kubectl rollout status deployment/{RELEASE_NAME}-api -n {NAMESPACE}
If you configured kasmZones, each zone creates its own set of Deployments with the zone name appended (for example, {RELEASE_NAME}-api-{ZONE_NAME}). Run kubectl get deployments -n {NAMESPACE} to list all Deployments in your namespace before restarting.
Applying Changes to a Running Instance
To apply changes to your values for a running deployment, always set dbManagement.initialize to false. If left enabled, the one-time initialization job will re-run and fail on an already-initialized database.
The value of dbManagement.upgrade.enable depends on whether the change includes a database schema migration. If applying a config change or image tag update with no schema migration (typical of rolling updates) you should set upgrade.enable to false in addition to your other changes:
dbManagement:
initialize: false
upgrade:
enable: false
If an image tag update includes a database schema migration (typical of major/minor version updates, e.g. 1.18.0 -> 1.18.1), then set the value to true:
dbManagement:
initialize: false
upgrade:
enable: true
When upgrade.enable: true is set, the upgrade job runs a full database backup and restore cycle on every helm upgrade, regardless of whether a schema migration is needed. It will not fail if there is nothing to migrate, but it will perform unnecessary work. Always reset upgrade.enable to false once the upgrade completes.
If you use false when a migration was required, pods will fail to start and the API logs will contain an alembic version mismatch error. Rerun the upgrade with upgrade.enable: true to recover, then reset it to false.
Then run:
helm upgrade {RELEASE_NAME} oci://registry-1.docker.io/kasmweb/kasm-helm \
--version 1.1190.0-develop -n {NAMESPACE} -f my-values.yaml
Offline Installations
If your cluster is unable to reach public registries, you may need to mirror the Kasm service images into a private registry and point the chart at it.
Get the Helm Chart
The offline machine needs a local copy of the Helm chart regardless of how the service images are transferred. On an internet-connected machine, clone the repository and package it for transfer:
git clone --branch develop https://github.com/kasmtech/kasm-helm.git
tar -cvf kasm-helm_develop.tar kasm-helm
Transfer the archive to the offline machine and unpack:
tar -xvf kasm-helm_develop.tar
This produces a kasm-helm/ directory with the chart at kasm-helm/charts/kasm-helm/ — the same layout used in the Modifying the chart locally tip in Step 5.
Get the Service Images
Choose one of the two approaches below. Both produce a service_images/ directory containing images.txt and one .tar per image, which the next step consumes directly.
Option A: Manual Image Export
The images.txt file in the cloned chart lists the service images you need to mirror:
cat kasm-helm/charts/kasm-helm/images.txt
kasmweb/api:develop
kasmweb/manager:develop
kasmweb/postgres:develop
kasmweb/proxy:develop
kasmweb/kasm-guac:develop
kasmweb/rdp-gateway:develop
kasmweb/rdp-https-gateway:develop
On an internet-connected machine, save each image into a service_images/ directory:
mkdir service_images
cp kasm-helm/charts/kasm-helm/images.txt service_images/
while IFS= read -r image; do
filename=$(echo "$image" | tr "/" "_" | tr ":" "_").tar
docker save "$image" > "service_images/$filename"
done < service_images/images.txt
Archive the directory and transfer it to the offline machine:
tar -czvf service_images.tar.gz service_images
On the offline machine, unpack so the service_images/ directory sits next to the kasm-helm/ directory from the previous step:
tar -xvf service_images.tar.gz
Option B: Pre-Packaged S3 Tarball (Recommended)
Kasm provides a pre-packaged archive containing all service images, which saves the time of exporting and compressing them individually. These are the same archives used by the single-server and multi-server offline installers. The published SHA256 for the download is listed under "Install Files" on the Single Server Installation page.
On an internet-connected machine, download the bundle:
curl -O https://kasmweb-build-artifacts.s3.amazonaws.com/kasm_backend/branches/develop/kasm_workspaces_service_images_develop_arm64.tar.gz
Transfer the downloaded .tar.gz to the offline machine and unpack it next to the kasm-helm/ directory from the previous step:
tar -xvf kasm_workspaces_service_images_develop_arm64.tar.gz
This produces a service_images/ directory:
total 3.7G
-rw-r--r-- 1 ubuntu ubuntu 193 Nov 14 16:11 images.txt
-rw------- 1 ubuntu ubuntu 190M Nov 14 16:12 kasmweb_agent_develop.tar
-rw------- 1 ubuntu ubuntu 1013M Nov 14 16:12 kasmweb_api_develop.tar
-rw------- 1 ubuntu ubuntu 763M Nov 14 16:13 kasmweb_kasm-guac_develop.tar
-rw------- 1 ubuntu ubuntu 1012M Nov 14 16:12 kasmweb_manager_develop.tar
-rw------- 1 ubuntu ubuntu 296M Nov 14 16:11 kasmweb_postgres_develop.tar
-rw------- 1 ubuntu ubuntu 113M Nov 14 16:13 kasmweb_proxy_develop.tar
-rw------- 1 ubuntu ubuntu 281M Nov 14 16:13 kasmweb_rdp-gateway_develop.tar
-rw------- 1 ubuntu ubuntu 55M Nov 14 16:13 kasmweb_rdp-https-gateway_develop.tar
Load and Push Images
From inside the service_images/ directory on the offline machine, load each image, tag it for your private registry, and push it:
cd service_images
while IFS= read -r image; do
filename=$(echo "$image" | tr "/" "_" | tr ":" "_").tar
docker load -i "$filename"
docker tag "$image" registry.example.com:5000/"$image"
docker push registry.example.com:5000/"$image"
done < images.txt
Point the Values File at Your Registry
Add registry overrides for each component to the my-values.yaml file you created in Step 3:
components:
proxy:
image:
registry: registry.example.com:5000
api:
image:
registry: registry.example.com:5000
manager:
image:
registry: registry.example.com:5000
guac:
image:
registry: registry.example.com:5000
rdpGateway:
image:
registry: registry.example.com:5000
rdpHttpsGateway:
image:
registry: registry.example.com:5000
database:
image:
registry: registry.example.com:5000
# if necessary, add imagePullSecrets to authenticate with registry.example.com
imagePullSecrets:
enabled: true
# if your pull secret already exists, populate the name of it here and omit username/password below
# name: "existing-pull-secret"
registry: "registry.example.com:5000"
username: "my-user"
# recommended to omit password here and instead provide it in your helm install command with "--set imagePullSecrets.password=my-password"
password: "my-password"
Install From the Local Chart
In place of the OCI / classic-repo commands in Step 5, install from the cloned repository you transferred:
cd kasm-helm
helm install {RELEASE_NAME} ./charts/kasm-helm -n {NAMESPACE} -f my-values.yaml
This matches the layout used by the Modifying the chart locally workflow in the standard install, so the rest of this guide applies unchanged.
Next Steps
- Configure TLS Certificates: Certificate management options
- Kubernetes How-To Guides: Multi-region proxies, VM migration, and more
- Upgrade Kasm on Kubernetes: Upgrade paths
- Kubernetes Troubleshooting: Diagnose pod, ingress, and storage issues