Skip to main content
Kubernetes Dashboard provides a built-in terminal for executing commands directly in running containers. This feature is invaluable for debugging, troubleshooting, and interactive exploration.

Overview

The Dashboard terminal uses WebSocket connections (via SockJS) to establish interactive shell sessions with your containers. This provides:
  • Interactive shell access (bash, sh, powershell, cmd)
  • Real-time command execution
  • Terminal resizing and scrolling
  • Multiple concurrent sessions
  • Secure communication over HTTPS
Shell access requires the container to have a shell binary installed. Common shells include bash, sh, powershell, and cmd.

Accessing the Terminal

There are multiple ways to open a shell session:
  1. Navigate to WorkloadsPods
  2. Click on a pod name
  3. Click the Exec button in the action bar
  4. Select the target container

Terminal Interface

The Dashboard terminal provides a full-featured shell environment:

Features

Interactive Shell

Full TTY support with command history and tab completion

Terminal Resize

Automatically adapts to browser window size

Copy/Paste

Standard clipboard operations for commands and output

Scrollback

Scroll through command history and output

Keyboard Shortcuts

  • Ctrl+C: Send interrupt signal (SIGINT)
  • Ctrl+D: Send EOF signal (exit shell)
  • Ctrl+L: Clear terminal screen
  • Arrow Up/Down: Navigate command history
  • Tab: Autocomplete commands and paths

Terminal Implementation

Dashboard implements terminal access using WebSockets and the Kubernetes exec API.

Session Management

Terminal sessions are managed via SockJS connections (modules/api/pkg/handler/terminal.go:49-69):
type TerminalSession struct {
    id            string
    bound         chan error
    sockJSSession sockjs.Session
    sizeChan      chan remotecommand.TerminalSize
}

type TerminalMessage struct {
    Op, Data, SessionID string
    Rows, Cols          uint16
}

Message Protocol

The terminal uses a message-based protocol:
OperationDirectionFieldsDescription
bindFrontend → BackendSessionIDBind SockJS session to terminal
stdinFrontend → BackendDataUser keystrokes and input
resizeFrontend → BackendRows, ColsTerminal window resized
stdoutBackend → FrontendDataProcess output
toastBackend → FrontendDataOut-of-band messages

Shell Detection

Dashboard automatically detects available shells (modules/api/pkg/handler/terminal.go:271-305):
func WaitForTerminal(k8sClient kubernetes.Interface, cfg *rest.Config, 
    request *restful.Request, sessionId string) {
    
    shell := request.QueryParameter("shell")
    
    var err error
    validShells := []string{"bash", "sh", "powershell", "cmd"}
    
    if isValidShell(validShells, shell) {
        cmd := []string{shell}
        err = startProcess(k8sClient, cfg, request, cmd, terminalSessions.Get(sessionId))
    } else {
        // Try shells until one succeeds
        for _, testShell := range validShells {
            cmd := []string{testShell}
            if err = startProcess(k8sClient, cfg, request, cmd, terminalSessions.Get(sessionId)); err == nil {
                break
            }
        }
    }
    
    if err != nil {
        terminalSessions.Close(sessionId, 2, err.Error())
        return
    }
    
    terminalSessions.Close(sessionId, 1, "Process exited")
}
If no shell is specified, Dashboard tries shells in order: bash, sh, powershell, cmd.

Process Execution

The terminal establishes a SPDY connection to the Kubernetes API (modules/api/pkg/handler/terminal.go:216-255):
func startProcess(k8sClient kubernetes.Interface, cfg *rest.Config, 
    request *restful.Request, cmd []string, ptyHandler PtyHandler) error {
    
    namespace := request.PathParameter("namespace")
    podName := request.PathParameter("pod")
    containerName := request.PathParameter("container")
    
    req := k8sClient.CoreV1().RESTClient().Post().
        Resource("pods").
        Name(podName).
        Namespace(namespace).
        SubResource("exec")
    
    req.VersionedParams(&v1.PodExecOptions{
        Container: containerName,
        Command:   cmd,
        Stdin:     true,
        Stdout:    true,
        Stderr:    true,
        TTY:       true,
    }, scheme.ParameterCodec)
    
    exec, err := remotecommand.NewSPDYExecutor(cfg, "POST", req.URL())
    if err != nil {
        return err
    }
    
    err = exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
        Stdin:             ptyHandler,
        Stdout:            ptyHandler,
        Stderr:            ptyHandler,
        TerminalSizeQueue: ptyHandler,
        Tty:               true,
    })
    
    return err
}

Common Use Cases

Debugging Application Issues

1

Open Shell

Execute into the problematic container
2

Inspect Environment

Check environment variables and configuration:
env | grep DATABASE
cat /etc/config/app.conf
3

Test Connectivity

Verify network connectivity to dependencies:
curl http://api-service:8080/health
ping database.default.svc.cluster.local
4

Review Files

Check application logs and data:
ls -la /var/log/
tail -f /var/log/app.log

Inspecting Container State

Common inspection commands:
ps aux
top

Testing Configuration Changes

Test configuration before deploying:
# Test database connection
psql -h database -U admin -d myapp -c "SELECT 1;"

# Validate configuration file
config-validator /etc/app/config.yaml

# Test API endpoint
curl -X POST http://localhost:8080/api/test \
  -H "Content-Type: application/json" \
  -d '{"test": "data"}'

Installing Debug Tools

Install troubleshooting utilities temporarily:
Temporary Changes Only: Changes made in a running container are lost when the container restarts. Update your container image for permanent changes.
# Debian/Ubuntu
apt update && apt install -y curl vim netcat

# Alpine
apk add --no-cache curl vim netcat-openbsd

# CentOS/RHEL
yum install -y curl vim nc

Security Considerations

Shell access requires the pods/exec permission:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-exec
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create", "get"]
Verify permissions:
kubectl auth can-i create pods/exec -n default
Terminal sessions should only be accessed over HTTPS to prevent command interception. The WebSocket connection inherits the HTTP(S) protocol.
Enable audit logging to track exec sessions:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
  verbs: ["create"]
  resources:
  - group: ""
    resources: ["pods/exec"]
Grant exec permissions only to trusted users and service accounts. Consider using Pod Security Policies or admission controllers to restrict exec access.

Troubleshooting

Check WebSocket connectivity: Ensure your network allows WebSocket connections and that no proxies are interfering.Verify pod status:
kubectl get pod <pod-name> -n <namespace>
The pod must be in “Running” state.Check container status: Ensure the container is running and healthy:
kubectl describe pod <pod-name> -n <namespace>
Error: “Process exited” or connection immediately closes.Cause: The container doesn’t have a shell binary.Solution: Use a debug container or update your image:
# Add shell to minimal images
RUN apk add --no-cache bash
Or use ephemeral debug containers (Kubernetes 1.23+):
kubectl debug -it <pod-name> --image=busybox --target=<container-name>
Check for blocking commands: Some commands may block indefinitely. Press Ctrl+C to interrupt.Verify network connectivity: Test the WebSocket connection by closing and reopening the terminal.Check browser console: Look for JavaScript errors or WebSocket connection issues.
Check RBAC permissions:
kubectl auth can-i create pods/exec --as=system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard -n <namespace>
Check pod security context: Some pods run as non-root users with restricted permissions. Use sudo if available, or exec as root:
# Not available in Dashboard UI - use kubectl
kubectl exec -it <pod-name> -n <namespace> -- /bin/bash -c "su - root"

Terminal Limitations

Be aware of Dashboard terminal limitations:
  • No file upload: Cannot upload files directly (use kubectl cp instead)
  • No session persistence: Sessions close when browser tab closes
  • No multiplexing: One terminal per browser tab
  • Limited scrollback: Browser memory constraints
For advanced terminal features, consider using:
  • kubectl exec: Full terminal features and file operations
  • k9s: Interactive Kubernetes CLI
  • Lens: Desktop Kubernetes IDE with built-in terminal

Best Practices

Prefer inspection over modification:
# Good: Read-only inspection
cat /etc/config/app.yaml

# Avoid: Modifying running containers
sed -i 's/old/new/' /etc/config/app.yaml
Make permanent changes via updated container images or ConfigMaps.
Keep notes of commands and findings for future reference and team collaboration.
Remove temporary files created during debugging:
rm /tmp/debug-output.txt
For distroless or minimal images, use debug containers instead of modifying running containers:
kubectl debug -it <pod-name> --image=ubuntu --target=<container-name>

Advanced Usage

Specifying Shell

Request a specific shell via URL parameter:
/api/v1/exec/<namespace>/<pod>/<container>?shell=bash
Supported shells:
  • bash: Bourne Again Shell
  • sh: POSIX shell
  • powershell: Windows PowerShell
  • cmd: Windows Command Prompt

Session Timeout

Sessions automatically close after 10 seconds if the WebSocket connection isn’t established (modules/api/pkg/handler/terminal.go:314-318):
case <-time.After(10 * time.Second):
    // Close chan and delete session when sockjs connection was timeout
    close(terminalSessions.Get(sessionId).bound)
    delete(terminalSessions.Sessions, sessionId)
    return

Next Steps

Viewing Logs

Access container logs for debugging

Managing Resources

Learn about resource management

Build docs developers (and LLMs) love