Mastering K9s: The Kubernetes CLI on Steroids

Intro

If you’re working with Kubernetes, you’ve probably experienced the pain of juggling multiple terminal windows, typing long kubectl commands, and constantly switching contexts. Enter K9s - a terminal-based UI that transforms how you interact with your Kubernetes clusters.

In this guide, I’ll show you how to level up your K9s skills with custom shortcuts, plugins, and configurations that will make your Kubernetes workflow smoother and more efficient.

Getting Started with K9s

K9s provides an intuitive terminal UI for navigating, observing, and managing your Kubernetes resources. If you haven’t installed it yet, you can get it from the official GitHub repository.

Insight: K9s is a terminal-based UI that simplifies Kubernetes cluster management. It provides a more interactive and efficient way to navigate, observe, and manage your Kubernetes resources compared to traditional kubectl commands.

Essential Hotkeys

Let’s start with the basics. These are the hotkeys you’ll use constantly:

  • SHIFT + ? - Help menu (your best friend when starting out)
  • SHIFT + : - Command mode (with tab autocompletion)
  • CTRL + r - Refresh the current view
  • / - Filter resources in the current view

Context and Namespace Navigation

In command mode, you can quickly switch contexts and namespaces:

:context <CONTEXT>   # or :ctx for short
:namespace <NAMESPACE>   # or :ns for short

Resource Management

  • e - Edit resource
  • y - View resource in YAML format
  • d - Describe resource (kubectl describe format)
  • CTRL + d - Delete resource (with confirmation)
  • CTRL + k - Delete resource (without confirmation - use with caution!)
  • c - Copy resource name
  • n - Copy current namespace

Pod-Specific Commands

When viewing pods:

  • l - View logs
    • f - Toggle fullscreen mode
    • w - Toggle line wrapping
    • a - Show logs from all containers
  • s - Shell into the pod
  • p - View logs from the previous pod instance

Deployment Commands

  • r - Rollout restart
  • Enter - View pods managed by this deployment
  • i - Set image

Helm Commands

  • v - View values
  • r - View releases

Secret Management

  • u - See which Pods or ServiceAccounts are using this Secret
  • x - Decode base64-encoded Secret values to human-readable text

Power User Customizations

Now let’s dive into how you can extend K9s to fit your workflow perfectly.

Custom Aliases

Here are the aliases I use in my K9s configuration:

# In ~/.k9s/aliases.yaml
aliases:
  sb: storage.cnrm.cloud.google.com/v1beta1/storagebuckets
  arr: artifactregistry.cnrm.cloud.google.com/v1beta1/artifactregistryrepositories
  bqds: bigquery.cnrm.cloud.google.com/v1beta1/bigquerydatasets
  fk: kustomizations flux-system
  ipm: iam.cnrm.cloud.google.com/v1beta1/iampolicymembers
  isa: iam.cnrm.cloud.google.com/v1beta1/iamserviceaccounts
  iua: imageupdateautomations
  ip: imagepolicies
  ir: image.toolkit.fluxcd.io/v1beta2/imagerepositories
  dp: apps/v1/deployments
  sec: v1/secrets
  jo: batch/v1/jobs
  cr: rbac.authorization.k8s.io/v1/clusterroles
  crb: rbac.authorization.k8s.io/v1/clusterrolebindings
  ro: rbac.authorization.k8s.io/v1/roles
  rb: rbac.authorization.k8s.io/v1/rolebindings
  np: networking.k8s.io/v1/networkpolicies
  mqu: rabbitmq.com/v1beta1/users

Insight: Aliases save you from typing long resource names and API versions. For example, typing :dp is much faster than :deployments or :apps/v1/deployments. The aliases above cover standard Kubernetes resources, RBAC objects, Flux CD components, and GCP-specific resources.

You can access these aliases in command mode with :alias.

Custom Keyboard Shortcuts

I’ve defined these keyboard shortcuts for quick access to resources:

# In ~/.k9s/hotkey.yml
hotKeys:
  # Hitting Shift-0 navigates to your pod view
  shift-0:
    shortCut: Shift-0
    description: Viewing pods
    command: pods
  # Hitting Shift-1 navigates to your deployments
  shift-1:
    shortCut: Shift-1
    description: View deployments
    command: dp
  # Hitting Shift-2 navigates to your xray deployments
  shift-2:
    shortCut: Shift-2
    description: ConfigMaps
    command: cm
  shift-3:
    shortCut: Shift-3
    description: ConfigMaps
    command: sec
  # Hitting "h" navigates to helm releases
  h:
    shortCut: h
    description: Helm Releases
    command: helmreleases
  # Hitting "k" navigates to all namespaces
  shift-k:
    shortCut: Shift-K
    description: All Kustomizations
    command: fk
  shift-E:
    shortCut: Shift-E
    description: Events
    command: events

Core Configuration

My K9s configuration file controls the behavior and appearance of the UI:

# In ~/.k9s/config.yaml
k9s:
  liveViewAutoRefresh: false
  refreshRate: 2
  maxConnRetry: 5
  ui:
    enableMouse: false
    headless: false
    logoless: false
    noIcons: false
    crumbsless: false
    # skin: catppuccin
    skin: nightfox
    reactive: true
  readOnly: false
  imageScans:
    enable: false
  noExitOnCtrlC: false
  shellPod:
    image: busybox:1.35.0
    namespace: default
    limits:
      cpu: 100m
      memory: 100Mi
  skipLatestRevCheck: false
  logger:
    tail: 100
    buffer: 5000
    sinceSeconds: 60
    #fullScreen: false
    textWrap: false
    showTime: false
  thresholds:
    cpu:
      critical: 90
      warn: 75
    memory:
      critical: 90
      warn: 75
  # screenDumpDir: $XDG_HOME/k9s/screendumps/
  disablePodCounting: false

Insight: This configuration optimizes K9s for performance and usability. The refreshRate: 2 provides a good balance between real-time updates and API server load. The shellPod configuration defines a lightweight container (busybox) for shelling into pods that don’t have a shell. The resource thresholds help identify problematic pods by highlighting them when they exceed CPU or memory thresholds.

Custom Views

I’ve customized the columns displayed for Flux CD Kustomizations:

# In ~/.k9s/views.yaml
views:
  kustomize.toolkit.fluxcd.io/v1/kustomizations:
    columns:
      - NAME
      - STATUS
      - SUSPENDED:.spec.suspend
      - DISABLED:.metadata.annotations.kustomize\.toolkit\.fluxcd\.io/reconcile
      - READY
      - AGE

Insight: Custom views are particularly valuable for Custom Resource Definitions (CRDs) like Flux CD Kustomizations. This view surfaces important information that’s normally buried in the resource spec, such as whether a Kustomization is suspended or has reconciliation disabled.

Plugins for Extended Functionality

Here are the plugins I use to extend K9s functionality:

# In ~/.k9s/plugin.yml
plugins:
  # View logs using stern
  stern:
    shortCut: Ctrl-L
    confirm: false
    description: "Logs <Stern>"
    scopes:
      - pods
      - deployments
    command: bash
    background: false
    args:
      - -c 
      - >- 
        stern $NAME -n $NAMESPACE --context $CONTEXT --color always 2>&1 | fzf --ansi --tail 1000 --tac --no-sort --exact --wrap
        --bind 'enter:execute:kubectl exec -it {1} -n $NAMESPACE --context $CONTEXT -- bash'
        --header 'Press CTRL-Y to copy selected line' --header-lines=1

Insight: The stern plugin combines the power of stern (multi-pod/container log viewing) with fzf (fuzzy finding) to create an interactive log experience. You can search logs in real-time, and even execute commands on pods directly from the log view by pressing Enter on a log line.

  # Edit decoded secrets
  edit-secret:
    shortCut: Ctrl-X
    confirm: false
    description: "Edit Decoded Secret"
    scopes:
      - secrets
    command: kubectl
    background: false
    args:
      - modify-secret
      - --namespace
      - $NAMESPACE
      - --context
      - $CONTEXT
      - $NAME

Insight: The edit-secret plugin simplifies secret management by allowing you to edit decoded secret values directly. This eliminates the need to manually decode, edit, and re-encode secrets.

  # View tree of resources created by selected Kustomization
  flux-tree:
    shortCut: t
    confirm: false
    scopes:
      - kustomizations
    description: Flux tree Kustomization
    command: bash
    background: false
    args:
      - -c
      - >-
        flux tree kustomization $NAME --context $CONTEXT -n $NAMESPACE | less -K

Insight: The flux-tree plugin visualizes the entire dependency tree of resources created by a Kustomization, making it easier to debug issues with GitOps deployments.

  # Get all suspended Kustomizations
  get-suspended-kustomizations:
    shortCut: Shift-S
    confirm: false
    description: Suspended Kustomizations
    scopes:
      - kustomizations
    command: sh
    background: false
    args:
      - -c
      - >-
        kubectl get
        --context $CONTEXT
        --all-namespaces
        kustomizations.kustomize.toolkit.fluxcd.io -o json
        | jq -r '.items[] | select(.spec.suspend==true) | [.metadata.name,.spec.suspend] | @tsv'
        | less -K
  # Check selected Kustomization suspend status
  kustomization-suspend-status:
    shortCut: s
    confirm: false
    description: Kustomization Suspend Status
    overwriteOutput: true
    scopes:
      - kustomizations
    command: sh
    background: true
    args:
      - -c
      - >-
        kubectl get kustomization $NAME
        --namespace $NAMESPACE
        --context $CONTEXT
        -o json | jq -r '.spec | if has("suspend") then "Suspended" else "Not Suspended" end' | less -K
  # Toggle reconcile annotation for Kustomization
  toggle-kustomization-annotation:
    shortCut: Shift-D
    confirm: true
    scopes:
      - kustomizations
    description: Toggle reconcile annotation for Kustomization
    command: bash
    background: true
    args:
      - -c
      - >-
        annotation="kustomize.toolkit.fluxcd.io/reconcile"; 
        current=$(kubectl --context $CONTEXT get kustomization -n $NAMESPACE $NAME -o=jsonpath="{.metadata.annotations['$annotation']}"); 
        if [[ "$current" == "disabled" ]]; then 
          kubectl --context $CONTEXT annotate kustomization -n $NAMESPACE $NAME $annotation-; 
        else 
          kubectl --context $CONTEXT annotate kustomization -n $NAMESPACE $NAME $annotation="disabled" --overwrite; 
        fi

Insight: This plugin toggles the reconciliation annotation on Kustomizations, allowing you to temporarily disable reconciliation without suspending the entire resource. This is useful when you want to make manual changes without Flux immediately overwriting them.

  # Suspend/Resume selected Kustomization
  toggle-kustomization:
    shortCut: Shift-T
    confirm: true
    scopes:
      - kustomizations
    description: Toggle to suspend or resume Kustomization
    command: bash
    background: true
    args:
      - -c
      - >-
        suspended=$(kubectl --context $CONTEXT get kustomizations -n $NAMESPACE $NAME -o=custom-columns=TYPE:.spec.suspend | tail -1); verb=$([[ $suspended == "true" ]] && echo "resume" || echo "suspend"); flux $verb kustomization --context $CONTEXT -n $NAMESPACE $NAME
  # Get all suspended HelmReleases
  get-suspended-helmreleases:
    shortCut: Shift-S
    confirm: false
    description: Suspended HelmReleases
    scopes:
      - helmreleases
    command: sh
    background: false
    args:
      - -c
      - >-
        kubectl get
        --context $CONTEXT
        --all-namespaces
        helmreleases.helm.toolkit.fluxcd.io -o json
        | jq -r '.items[] | select(.spec.suspend==true) | [.metadata.name,.spec.suspend] | @tsv'
        | less -K
  # Suspend/Resume selected HelmRelease
  toggle-hr:
    shortCut: Shift-T
    confirm: true
    scopes:
      - helmreleases
    description: Toggle to suspend or resume HelmRelease
    command: bash
    background: true
    args:
      - -c
      - >-
        suspended=$(kubectl --context $CONTEXT get hr -n $NAMESPACE $NAME -o=custom-columns=TYPE:.spec.suspend | tail -1); verb=$([[ $suspended == "true" ]] && echo "resume" || echo "suspend"); flux $verb hr --context $CONTEXT -n $NAMESPACE $NAME
  # Reconcile selected Kustomization
  reconcile-ks:
    shortCut: r
    confirm: false
    description: Flux reconcile
    scopes:
      - kustomizations
    command: bash
    background: true
    args:
      - -c
      - "flux --context $CONTEXT reconcile kustomization -n $NAMESPACE $NAME"
  # Reconcile selected Kustomization with force
  reconcile-ks-force:
    shortCut: Shift-F
    confirm: false
    description: Flux reconcile Kustomization force
    scopes:
      - kustomizations
    command: bash
    background: true
    args:
      - -c
      - "flux --context $CONTEXT reconcile ks -n $NAMESPACE $NAME --force"

Insight: The force reconciliation plugin is valuable when you need to override drift detection and force resources to match the desired state in Git, even if Kubernetes reports them as already in sync.

  # Reconcile selected Kustomization with source
  reconcile-ks-with-source:
    shortCut: Shift-R
    confirm: false
    description: Flux reconcile with source
    scopes:
      - kustomizations
    command: bash
    background: true
    args:
      - -c
      - "flux --context $CONTEXT reconcile kustomization -n $NAMESPACE $NAME --with-source"

Insight: This plugin reconciles both the Kustomization and its source (like a GitRepository), ensuring that the latest changes from Git are pulled before reconciling. This is useful when you’ve just pushed changes and want to apply them immediately.

  # Reconcile selected HelmRelease
  reconcile-hr:
    shortCut: r
    confirm: false
    description: Flux reconcile HelmRelease
    scopes:
      - helmreleases
    command: bash
    background: true
    args:
      - -c
      - "flux --context $CONTEXT reconcile hr -n $NAMESPACE $NAME"
  # Reconcile selected HelmRelease with force
  reconcile-hr-force:
    shortCut: Shift-F
    confirm: false
    description: Flux reconcile HelmRelease force
    scopes:
      - helmreleases
    command: bash
    background: true
    args:
      - -c
      - "flux --context $CONTEXT reconcile hr -n $NAMESPACE $NAME --force"
  # Reconcile selected HelmRelease with source
  reconcile-hr-with-source:
    shortCut: Shift-R
    confirm: false
    description: Flux reconcile HelmRelease with-source
    scopes:
      - helmreleases
    command: bash
    background: true
    args:
      - -c
      - "flux --context $CONTEXT reconcile hr -n $NAMESPACE $NAME --with-source"
  # Reconcile selected ImageRepository
  reconcile-image-repository:
    shortCut: Shift-R
    confirm: false
    description: Flux reconcile ImageRepository
    scopes:
      - imagerepositories
    command: bash
    background: true
    args:
      - -c
      - "flux --context $CONTEXT reconcile image repository -n $NAMESPACE $NAME"

Insight: For Flux image automation, this plugin forces an immediate scan of an ImageRepository, which is useful when you’ve just pushed a new image and want Flux to detect it without waiting for the normal scan interval.

  # Reconcile selected ImageUpdateAutomation
  reconcile-image-update-automation:
    shortCut: Shift-R
    confirm: false
    description: Flux reconcile ImageUpdateAutomation
    scopes:
      - imageupdateautomation
    command: bash
    background: true
    args:
      - -c
      - "flux --context $CONTEXT reconcile image update -n $NAMESPACE $NAME"

Insight: This plugin triggers the image update automation process, which checks for new images and updates the manifests in Git. It’s useful when you want to immediately apply image updates without waiting for the automation interval.

Pro Tips

Sorting and Filtering

K9s offers powerful sorting and filtering capabilities:

  • SHIFT-c - Sort by CPU usage
  • CTRL-q - Sort by memory usage as a percentage of limits
  • CTRL-z - Show only resources with errors (CrashLoopBackOff, Pending, etc.)

Custom Views

You can customize the columns displayed for different resources by editing the ~/.k9s/views.yaml file. This is especially useful for custom resources where you want to highlight specific fields.

Conclusion

K9s transforms the Kubernetes command-line experience into something much more interactive and efficient. By customizing it with aliases, hotkeys, and plugins, you can create a workflow that perfectly matches your needs.

The best part? All these customizations are stored in simple YAML files that you can version control and share with your team.