derailed/k9s · error
unable to launch shell pod on node %s
Error message
unable to launch shell pod on node %s
What it means
Node-shell flow: k9s creates a privileged helper pod (k9s-<pid>) on the target node and polls its Status.Phase in a retry loop with k9sShellRetryDelay until Running or ctx.Done(). This error means the pod never reached Running within the wait window — the pod is Pending/ImagePullBackOff/CrashLoopOff, or the context/timeout expired first.
Source
Thrown at internal/view/exec.go:467
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod); err != nil {
return err
}
slog.Debug("Checking k9s shell pod retries",
slogs.Retry, i,
slogs.PodPhase, pod.Status.Phase,
)
if pod.Status.Phase == v1.PodRunning {
return nil
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(k9sShellRetryDelay):
}
}
return fmt.Errorf("unable to launch shell pod on node %s", node)
}
func k9sShellPodName() string {
return fmt.Sprintf("%s-%d", k9sShell, os.Getpid())
}
func k9sShellPod(node string, cfg *config.ShellPod) *v1.Pod {
var grace int64
var priv = true
slog.Debug("Shell pod config", slogs.ShellPodCfg, cfg)
c := v1.Container{
Name: k9sShell,
Image: cfg.Image,
ImagePullPolicy: cfg.ImagePullPolicy,
VolumeMounts: []v1.VolumeMount{
{
Name: "root-vol",View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Check the helper pod directly: kubectl get pod k9s-<pid> -n kube-system and kubectl describe it — events show ImagePullBackOff, Unschedulable, or admission denial
- Set an image reachable from the cluster in k9s config: shellPod.image (e.g. an internal registry mirror)
- Verify the node is schedulable (kubectl uncordon <node>) and has capacity
- Grant the user pod-create permissions in kube-system if RBAC is the blocker
Example fix
# before (k9s config.yaml — default image unreachable in air-gapped cluster)
k9s:
shellPod: {}
# after — point at an in-cluster reachable image
k9s:
shellPod:
image: registry.internal/mirror/alpine:3.20
command: ["/bin/bash"]
namespace: kube-system Defensive patterns
Strategy: retry
Validate before calling
// preflight: confirm the shell image is pullable and node schedulable before shelling
if err := kubectl.CanPull(ctx, cfg.Image); err != nil {
return fmt.Errorf("shell image %s not pullable: %w", cfg.Image, err)
}
if nodeCordoned(node) {
return fmt.Errorf("node %s is cordoned", node)
} Prevention
- Set shellPod.image to a registry reachable from every cluster you manage
- Check kubectl get pod k9s-<pid> -n kube-system events on first failure — it names the real cause
- Uncordon nodes and ensure capacity before using node shell
- Retry once after slow image pulls; first pull can exceed the wait window
When it happens
Trigger: Pressing the node shell key with featureGates.nodeShell enabled when the shell pod image cannot be pulled (air-gapped registry), the node is unschedulable/cordoned/full (Pending), RBAC blocks pod creation in kube-system, or the launch is slow enough to blow the context timeout.
Common situations: Air-gapped clusters that cannot pull the default shell image; custom shellPod image misconfigured in k9s config; nodes with taints tolerating nothing; strict PodSecurity/Admission blocking the privileged pod.
Related errors
- node is cordoned
- no node assigned
- expecting a maintainer for %q
- namespace not ready
- expecting a switchable resource
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/c930080491a2825c.
Report an issue: GitHub.