K9s is a great Kubernetes CLI which enables to navigate a bit more friendly in the cluster than using kubectl or any HTTP client of the Kubernetes API. A small hidden gem is the ability to set a them (colors mainly) per context (environment). Let see how to set it up to not do a dev operation on your prod cluster!

K9s got a lot of changes in its configuration, moving from config.yml to config.yaml for its main configuration to moving in different folders for clusters configuration for example. These changes are kind of justified by a better structure but they have one big drawback: most of the docs are outdated. Another pitfall is that depending the OS you don’t have the exact same folder structure (XDG folder).

Since this took me more time than I expected to find how to tune the skin of K9s per context, let me explain how I did it at the end.

this post should work on Ubuntu - and most Linux - and with K9s >= 0.29 but it was tested with version v0.32.4 and Ubuntu 22.04.

Context

A quick reminder about contexts.

A common kubeconfig configuration file will look like:

~/.kube/config
apiVersion: v1
clusters:
- cluster: (1)
    certificate-authority-data: ...
    server: https://my.k8s.api
  name: my-cluster
users:
- name: user (2)
  ....
contexts:
- context: (3)
    cluster: my-cluster
    user: user
  name: my-ctx
current-context: "my-ctx" (4)
kind: Config
1 Define a cluster in the list of clusters, a cluster is mainly a HTTP client specification to connect to Kubernetes API (URL and certificate),
2 Define an "user" in the list of users, an user is mainly an authentication mechanism (a bearer token, a mTLS configuration, …​),
3 Define a context in the list of clusters, a cluster is just a link between a cluster and an user, it is literally a complete HTTP client configuration to be allowed to do API calls on Kubernetes,
4 (optional) Define the default ("current") context, i.e. the context to use if not is explicitly set on the command line.
this is the current-context that k9s will use if you don’t launch it with --context option.

Context management

As you probably know or guessed reading previous part, you can put X clusters, Y users and Z contexts in the same kubeconfig file. In other words you can define your prod, preprod, qa, benchmark and dev clusters in the same kubeconfig file, this works.

However, I’m not very happy with that solution since, if you rely on current-context you can not use the right context and you can mess up an environment.

For that reason, I always split my environment in separated files. Personally I use the pattern ~/.kube/$org/$env/config - if you work for a single organization no need to use the $org layer. The other tip with this setup is to ensure any config file has an unique identifier for the context name of every environment:

~/.kube/yupiik/dev/config
...
contexts:
- context:
    cluster: dev
    namespace: default
    user: user
  name: dev (1)
current-context: dev (1)
clusters:
- cluster:
    ...
  name: dev (2)
1 The context of the dev config file is named dev,
2 As a convention I also name my single cluster the same way but this is optional.

Indeed, this is a personal choice : I prefer to switch between environments using export KUBECONFIG=~/.kube/xxx/yyy/config and keep the current-context to the single context of the file which is compatible with any tool reading KUBECONFIG environment variable - including the great Yupiik BundleBee.

K9S

K9S, as any Kubernetes context consumer relies on current-context by default.

On - a standard - Ubuntu, the default global configuration of k9s is located at ~/.config/k9s/config.yaml. In this file you can set a default skin/theme:

~/.config/k9s/config.yaml
k9s:
  ...
  ui:
    skin: skin_name (1)
    ...
  skipLatestRevCheck: false
  ...
1 Enable to set the global default theme name.

Now, K9s reading by default the contextual KUBECONFIG - or default one in ~/.kube/config - it will extract the default current-context if not empty and load it. Indeed since it loads the context it also knows about the cluster since the context references it. It will complete that by trying to load a context specific k9s configuration file but it is not more in the global config.yaml, it is now in ~/.local/share/k9s/clusters/$clusterName/$contextName/config.yaml.

personally, since I have one config file per environment, I name m context and cluster the same which simplifies a lot this setup.

In this cluster specific file you can set a skin specific for this cluster+context - or environment if you followed my convention:

~/.local/share/k9s/clusters/$clusterName/$contextName/config.yaml
k9s:
  skin: dev (1)
  namespace:
    active: all
    lockFavorites: false
    favorites: (2)
    - app
    - fusion
    - bundlebee
    - all
    - default
  ...
1 Set the skin to use,
2 Not related to skin but enable to list the "favorites" namespaces, it is convenient to tune it to get your known namespaces prefilled in K9s.

The skin is looked in from global config.yaml directory in the subdirectory skins so here we named it as the context (dev in this case) but it could be green or one of the default K9s skins. For custom skins, you must put the file in ~/.config/k9s/skins/$skinName.yaml - where $skinName is the environment name.

You can tune all colors but I tend to tune the body background since it will keep the visibility of the "content" part of the UI but still highlight on which environment you are - but feel free to tune it more:

~/.config/k9s/skins/$skinName.yaml
k9s:
  body:
    bgColor: "default" (1)
  ...
1 The body background (color of the header and space around the "content" pane).

The value of the color is basically an "almost-CSS" value and it can even if an hexa color (bgColor: "#008800" for example but NOT #080). Alternatively you can use named colors - see K9s color names for the index.

transparent "color" is the "default" and can be set using default keyword.

Personally I create one skin per environment (dev, qa, preprod, prod, …​) and I just reference it in the specific cluster/context configuration files to use the same convention of colors over organizations - otherwise it becomes a nightmare to know where you are.

Conclusion

In this post we saw how to configure K9s to get one skin - background color - per environment and ensure you immediately know when checking your pods, logs, …​ that you are working on production or in a dev environment.

This kind of setup can look "g33ky" but is actually quite helping - as any visual management - to save time and more importantly human errors so I would highly recommend you to do it.

A similar idea is to ensure any machine you can connect has a custom PS1 prompt where the environment is appended to the prompt and a different color as well - ideally the same than in K9s.

Long story short: make your life easier and you’ll work faster and happier :).

From the same author:

In the same category: