Skip to main content

Workload Injection

In the injection model the Provisioner patches a running workload — a Deployment, StatefulSet, or DaemonSet — rather than creating a new pod. The workspace container is added to the workload's pod template alongside the workload's own containers, sharing its network and PID namespaces.

The k8shell-workspace Helm chart is used to render workspace resource manifests but is not installed as a Helm release — the Provisioner renders it locally and merges the relevant resources directly into the workload.

warning

k8shelld listens on TCP port 2822. If the workload has network policies that restrict ingress, they must explicitly allow ingress on port 2822, otherwise the workspace will be unreachable.

Inject

The Provisioner appends an init container and a workspace container to the workload's pod template, along with any required volumes. ConfigMaps and PVCs defined in the blueprint are created in the workload's namespace (home-directory volumes and secret-backed volumes are excluded). All injected container and volume names are prefixed with the workspace canonical ID to avoid naming conflicts with existing workload resources. shareProcessNamespace: true is set on the pod spec.

The injection state — injected container names, volume names, and the original shareProcessNamespace value — is recorded in a k8shell.io/injection annotation on the workload object. The workload controller rolls out updated pods containing the workspace container, after which the workspace is reachable via the SSH Proxy and API Server.

Eject

Ejecting reverses the injection. The Provisioner reads the k8shell.io/injection annotation, removes all injected containers, init containers, and volumes from the pod template, restores the original shareProcessNamespace value, and deletes the associated ConfigMaps and PVCs. The workload controller rolls out clean pods. Shared PVCs are retained and must be cleaned up separately.

Limitations

Three workspace capabilities are unavailable in injection mode, each for a structural reason tied to how injection works.

No home directory storage. Blueprint-defined persistent volumes scoped to the user home directory are not created or mounted. A persistent volume can only be exclusively owned by a single writer at a time — in a standalone pod this is guaranteed because the pod is unique. In injection mode the workload may scale to multiple replicas, meaning multiple workspace instances would attempt to own the same volume simultaneously with no coordination, risking data corruption.

No Podman sidecar. The Podman sidecar is not injected into workload pods. Injection workspaces are designed for debugging running services — container build capabilities serve a different purpose. Additionally, the Podman sidecar relies on a node-local persistent volume for its graph directory, which cannot be reliably managed across the replicas of a workload the Provisioner does not control.

Network policies not applied. Network policies defined in the blueprint are not created. The workspace container joins the existing pod's network namespace, which is governed by the workload's own cluster policies. The Provisioner has no authority to modify or extend those policies after the fact.

Metadata

The Provisioner sets metadata on both the workload pods and the workload object when injection is active.

Pod metadata

Injected pods retain their original names (generated by the workload controller) and receive the following additional labels and annotations.

Annotations

AnnotationDescription
k8shell.io/userstrBase64-encoded user string identifying the workspace owner.

Labels

LabelDescription
k8shell.io/blueprintName of the blueprint used to provision the workspace.
k8shell.io/canonical-idCanonical ID of the workspace in the form <username>-<short-hash>.
k8shell.io/injectedSet to true on all pods that have an active workspace injection.
k8shell.io/job-idUUID of the provisioning job that created the workspace.
k8shell.io/organizationOrganization the workspace owner belongs to.
k8shell.io/usernameUsername of the workspace owner.
k8shell.io/workload-kindKind of the injected workload in lowercase: deployment, statefulset, or daemonset.
k8shell.io/workload-nameName of the injected workload.

Workload metadata

The Provisioner records the full injection state on the workload object via a single annotation, which is read back during eject to reverse the operation precisely.

Annotations

AnnotationDescription
k8shell.io/injectionJSON object recording the injection state: canonicalId, injected containers, initContainers, and volumes (all prefixed with the canonical ID), and the original sharePID value before injection.