Skip to main content
Version: 1.19.0 (latest)

Migrate from VM to Kubernetes

This guide walks you through migrating your existing VM-based Kasm deployment to Kubernetes.

note

Ensure your VM deployment is on Kasm 1.19.0 before migrating. If you are on an older version, upgrade your VM deployment first.

warning

Scope: This guide covers the migration of the Kasm control plane, including primary zone dedicated and connection proxies. Agents and additional zone dedicated and connection proxies are not part of the Helm chart and must be migrated or reinstalled separately after the control plane is up.

Choose the section that matches your current database setup:

  • Included DB — your VM uses Kasm's built-in PostgreSQL container (database.standalone=false)
  • Standalone DB — your VM connects to an external PostgreSQL database (database.standalone=true)

Migrating with Included DB

Use this path if your VM deployment uses Kasm's built-in PostgreSQL container.

warning

This migration requires downtime. Your existing Kasm deployment will be unavailable during the process. Plan accordingly and schedule a maintenance window.

At a Glance

  1. Back up the database on the VM
  2. Preserve your Kasm secrets
  3. Transfer the backup to Kubernetes
  4. Install the Kasm Helm chart
  5. Confirm the DB is running
  6. Restore the database
  7. Verify and log in

1. Back Up the Database on the VM

Follow the official Kasm documentation to take a database backup on your VM:

Rename the backup file to kasm_dump.tar once complete.


2. Preserve Your Kasm Secrets

To retain the same credentials after migration, create the Kasm secrets in your target namespace before installing the chart.

Copy the file examples/kasm-secrets.yaml from our chart source and populate it with the credential values from your VM deployment:

warning

The secret name must be {RELEASE_NAME}-secrets, where {RELEASE_NAME} is the name you will use in your helm install command. Update the name field in kasm-secrets.yaml accordingly before applying.

warning

All secret values must be base64 encoded. Encode a value with: echo -n {secret-value} | base64

kubectl create namespace {NAMESPACE}
kubectl apply -f kasm-secrets.yaml -n {NAMESPACE}

Verify the values are correct:

kubectl -n {NAMESPACE} get secrets/{RELEASE_NAME}-secrets \
--template='{{ range $key, $value := .data }}{{ printf "%s: %s\n" $key ($value | base64decode) }}{{ end }}'

3. Transfer the Backup to Kubernetes

Download db-upload.yaml and update the following fields before applying:

FieldValue
imageReplace with your current VM Kasm version, e.g. kasmweb/api:1.19.0
storageClassNameCommented out by default to use the namespace default. Uncomment and set a value to use a specific storage class
claimNamePVC name for the backup. Keep default (kasm-db-dump-pvc) unless you need a custom name — if changed, update claimName in db-restore.yaml to match

Deploy the upload pod to create a PVC and receive the backup file:

kubectl apply -f db-upload.yaml -n {NAMESPACE}

Copy your backup file into the pod:

kubectl cp /path/to/kasm_dump.tar {NAMESPACE}/{UPLOAD_POD_NAME}:/data/kasm-db-dump/kasm_dump.tar

Replace {UPLOAD_POD_NAME} with the actual pod name. Retrieve it with:

kubectl get pods -n {NAMESPACE}

Monitor the upload pod logs to confirm the transfer is complete:

kubectl logs -f {UPLOAD_POD_NAME} -n {NAMESPACE}

Expected output:

Waiting for DB file upload...
File uploading.
⏳ Upload in progress... (size: 150240768 bytes)
✅ File upload complete. Final size: 206191616 bytes

Verify the Final size matches the size of your local backup file exactly before proceeding.


4. Install the Kasm Helm Chart

Install your TLS certificate into the namespace before installing the chart. Refer to Configure TLS Certificates for Kasm on Kubernetes for the available methods. Note the secret name you create — you will need it in my-values.yaml below.

Create your my-values.yaml. Refer to the chart documentation for all available configuration options, including ingress settings. At minimum, include:

publicAddr: kasm.contoso.com
certificate:
secretName: {CERTIFICATE_SECRET_NAME} # The TLS secret name you created above
dbManagement:
initialize: false
warning

Set dbManagement.initialize: false. Enabling it will pre-populate the schema and conflict with the restore in Step 6.

Install the chart:

warning

The {RELEASE_NAME} used here must match the secret name you applied in the previous step. For example, if your secret is named kasm-secrets, your release name must be kasm.

helm install {RELEASE_NAME} oci://registry-1.docker.io/kasmweb/kasm-helm \
--version 1.1190.0 -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 -n {NAMESPACE} -f my-values.yaml

5. Confirm the DB is Running

Watch the pods until the DB is ready:

kubectl get pods -n {NAMESPACE} --watch

Example output:

kasm-api-default-7985f66df6-pnhng 0/1 Init:0/1 0 53s
kasm-db-1-19-0-0 1/1 Running 0 52s
kasm-guac-6d44dd8844-zmpzv 0/1 Init:0/1 0 53s
kasm-manager-default-585f45f8df-h8n6z 0/1 Init:0/2 0 53s
kasm-proxy-default-9c98f99f8-k69wt 0/1 Init:0/4 0 52s
kasm-rdp-gateway-f9996c885-n74h5 0/1 Init:0/1 0 52s
kasm-rdp-https-gateway-6f5d456b59-67hxj 0/1 Init:0/1 0 52s

Once the {RELEASE_NAME}-db-* pod shows Running, press Ctrl+C to stop watching and proceed to the next step.


6. Restore the Database

Download db-restore.yaml and update the following fields:

FieldValue
imageReplace with your current VM Kasm version, e.g. kasmweb/api:1.19.0
POSTGRES_HOSTDB service name. Run kubectl -n {NAMESPACE} get service to find it
POSTGRES_DBDefaults to kasm. Only change if you set database.kasmDbName in my-values.yaml
POSTGRES_USERDefaults to kasmapp. Only change if you set database.kasmDbUser in my-values.yaml
secretKeyRef.nameMust be {RELEASE_NAME}-secrets, where {RELEASE_NAME} is your Helm release name
secretKeyRef.keyDefaults to db-password — auto-generated by the Helm chart. Keep default
claimNameDefaults to kasm-db-dump-pvc. Keep default unless you changed it in db-upload.yaml

Apply the restore job:

kubectl apply -n {NAMESPACE} -f db-restore.yaml
kubectl logs -f job/kasm-db-restore -n {NAMESPACE}

Expected output when the restore completes:

Restoring database from /data/kasm-db-dump/kasm_dump.tar
Restore complete

7. Verify and Log In

After the restore completes, the pods will gradually come into a running state. Watch until all pods are running:

kubectl get pods -n {NAMESPACE} --watch

Once all pods are running, press Ctrl+C to stop watching.

If you enabled ingress in your my-values.yaml, update your DNS record for publicAddr to point to the ingress address. Retrieve it with:

kubectl get ingress -n {NAMESPACE}

Use the value in the ADDRESS column. Once DNS has propagated, access your Kasm environment using the publicAddr you set. For credentials, run:

helm get notes {RELEASE_NAME} -n {NAMESPACE}

Migrating with Standalone DB

Use this path if your VM deployment connects to an external PostgreSQL database. Since your database is not managed by Kasm, it will continue running independently.

warning

This migration requires downtime. Your existing Kasm deployment will be unavailable during the process. Plan accordingly and schedule a maintenance window.

At a Glance

  1. Back up the database
  2. Preserve your Kasm secrets
  3. Install the Kasm Helm chart
  4. Verify and log in

1. Back Up the Database

Follow the official Kasm documentation to take a backup of your standalone PostgreSQL database:

Store the backup in a safe location before proceeding.

Once the backup is complete, stop all Kasm services on the VM to prevent any further writes to the database:

sudo /opt/kasm/current/bin/stop

2. Preserve Your Kasm Secrets

To retain the same credentials after migration, create the Kasm secrets in your target namespace before installing the chart.

Edit and apply kasm-secrets.yaml with the credential values from your VM deployment:

warning

The secret name must be {RELEASE_NAME}-secrets, where {RELEASE_NAME} is the name you will use in your helm install command. Update the name field in kasm-secrets.yaml accordingly before applying.

warning

All secret values must be base64 encoded. Encode a value with: echo -n {secret-value} | base64

kubectl create namespace {NAMESPACE}
kubectl apply -f kasm-secrets.yaml -n {NAMESPACE}

Verify the values are correct:

kubectl -n {NAMESPACE} get secrets/{RELEASE_NAME}-secrets \
--template='{{ range $key, $value := .data }}{{ printf "%s: %s\n" $key ($value | base64decode) }}{{ end }}'

3. Install the Kasm Helm Chart

Install your TLS certificate into the namespace before installing the chart. Refer to Configure TLS Certificates for Kasm on Kubernetes for the available methods. Note the secret name you create — you will need it in my-values.yaml below.

Create your my-values.yaml. Refer to the chart documentation for all available configuration options, including ingress settings. At minimum, include:

publicAddr: kasm.contoso.com
certificate:
secretName: {CERTIFICATE_SECRET_NAME} # The TLS secret name you created above
database:
standalone: true
hostname: "{DB_HOSTNAME}" # <-- REPLACE with your standalone DB hostname
port: 5432 # Change if your PostgreSQL instance uses a non-default port
kasmDbName: kasm # Default VM installation uses "kasm". Change if you used a different database name
kasmDbUser: kasmapp # Default VM installation uses "kasmapp". Change if you used a different database user
dbManagement:
initialize: false

Install the chart:

warning

The {RELEASE_NAME} used here must match the secret name you applied in the previous step. For example, if your secret is named kasm-secrets, your release name must be kasm.

helm install {RELEASE_NAME} oci://registry-1.docker.io/kasmweb/kasm-helm \
--version 1.1190.0 -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 -n {NAMESPACE} -f my-values.yaml

4. Verify and Log In

The pods will gradually come into a running state. Watch until all pods are running:

kubectl get pods -n {NAMESPACE} --watch

Once all pods are running, press Ctrl+C to stop watching.

If you enabled ingress in your my-values.yaml, update your DNS record for publicAddr to point to the ingress address. Retrieve it with:

kubectl get ingress -n {NAMESPACE}

Use the value in the ADDRESS column. Once DNS has propagated, access your Kasm environment using the publicAddr you set. For credentials, run:

helm get notes {RELEASE_NAME} -n {NAMESPACE}

Troubleshooting

See Kubernetes Troubleshooting for assistance.