Installation

Kube Workspaces can be deployed to any Kubernetes cluster using four supported methods: a local kind cluster for development, and Kustomize, Helm, or ArgoCD for production. Every method deploys the same published images and CRDs — pick the one that matches how you manage the rest of your cluster.

All installs below deploy without authentication (it is opt-in and off by default). See Authentication to enable OIDC or local auth after deploying. Before going to production, set your own hostnames — the manifests use the placeholder domain workspaces.example.com, which is not issued by real ACME providers; see Customizing your domain.

Prerequisites

  • kubectl with access to your target cluster
  • kind — only for the local cluster method
  • helm 3.8+ — only for the Helm method

No images need to be built: the manifests reference the published ghcr.io/kube-workspaces/* images.

Optional — VM workspaces. spec.type: vm requires KubeVirt to be installed in the cluster first (it is not bundled). On a local/kind cluster without nested virtualization, enable software emulation in the KubeVirt CR (spec.configuration.developerConfiguration.useEmulation: true). The controller detects the KubeVirt CRDs and reports a KubeVirtNotInstalled status condition on any vm workspace created without them; container and scratch workspaces work regardless.

CRDs must use server-side apply. The Workspace CRD embeds a full Kubernetes PodSpec and is ~658 KiB, far over the 256 KiB last-applied-configuration annotation limit. Plain kubectl apply -f fails on it — every method below applies the CRDs with --server-side (or its ArgoCD equivalent).

Local cluster (kind)

Deploy to a throwaway local cluster for development:

kind create cluster

make install-crd           # CRDs (server-side apply)
make deploy-kustomize      # components
make install-images        # workspace image catalog
make port-forward-frontend # UI on localhost:3000

Open http://localhost:3000. No login is required.

The Ingress in kustomize/base hardcodes ingressClassName: traefik and a placeholder hostname, so it is inert on a default kind cluster — port-forwarding is the only way in.

To tear everything down:

kind delete cluster

Kustomize

Apply the CRDs first, then the components. Both must use server-side apply:

kubectl apply --server-side -k kustomize/crds/
kubectl apply --server-side -k kustomize/base/

kustomize/base does not create any Image CRs — without the catalog the UI is empty. Install it separately:

kubectl apply --server-side -f images.yaml   # or: make install-images

Set your own hostnames with a kustomize overlay before going to production — see Customizing your domain.

Helm

The chart ships the CRDs and, by default, a curated catalog of example images (including Alpine and Debian VM images for spec.type: vm).

From the published chart, without cloning this repository:

helm install kube-workspaces \
  oci://ghcr.io/kube-workspaces/charts/kube-workspaces \
  --namespace kube-workspaces-system --create-namespace

From this repository:

helm install kube-workspaces helm/kube-workspaces/ \
  --namespace kube-workspaces-system --create-namespace

Workspace images

Setting Default Effect
installExampleImages true Curated set of example images (incl. Alpine + Debian VMs)
installCatalogImages false Install the full vendored catalog instead
images [] Add your own Image CRs regardless of the catalog setting

For example, to install the full catalog:

helm install kube-workspaces oci://ghcr.io/kube-workspaces/charts/kube-workspaces \
  --namespace kube-workspaces-system --create-namespace \
  --set installCatalogImages=true

Installing into an existing namespace

Installing into a namespace managed elsewhere requires disabling release namespace creation — Helm cannot adopt a namespace it did not create:

helm install kube-workspaces oci://ghcr.io/kube-workspaces/charts/kube-workspaces \
  --namespace my-shared-namespace \
  --set namespaces.createReleaseNamespace=false

Without this, the install fails with invalid ownership metadata. The workspace namespace is still created; control it with namespaces.createWorkspaceNamespace.

ArgoCD

Argo CD deploys from the git remote, so it syncs the last pushed commit rather than your local working tree.

Apply the CRDs Application first — the components Application will not sync cleanly against missing CRDs:

kubectl apply -f argocd/application-crds.yaml
kubectl apply -f argocd/application.yaml

The CRDs Application syncs with Replace=true and the components Application with ServerSideApply=true, matching the kustomize requirements above.

Neither Application creates Image CRs (they point at kustomize/crds and kustomize/base). To populate the UI catalog, apply images.yaml with kubectl or via a third Application pointing at https://github.com/kube-workspaces/deploy with images.yaml as a file source.

Verify the deployment

Every method lands in the kube-workspaces-system namespace. Confirm the CRDs are established and all four deployments are available:

kubectl wait --for=condition=Established \
  crd/workspaces.kubeworkspaces.io crd/images.kubeworkspaces.io \
  crd/users.kubeworkspaces.io crd/authconfigs.kubeworkspaces.io \
  crd/platformconfigs.kubeworkspaces.io crd/poddefaults.kubeworkspaces.io \
  --timeout=60s

kubectl wait --for=condition=Available deployment --all \
  -n kube-workspaces-system --timeout=300s

Then access the UI:

kubectl port-forward -n kube-workspaces-system svc/kube-workspaces-frontend 3000:80

Open http://localhost:3000.

Next steps