--- title: "K9s Per-Cluster Configuration" date: 2025-06-08 draft: false category: guide excerpt: "A Bash script that gives every Kubernetes context its own K9s background colour, driven by a small YAML file. Keep each environment visually distinct and harder to accidentally destroy." --- # Aim the gun away from your foot please [repo-large:WillMatthews/k9s-config] [K9s](https://k9scli.io/) is a fabulously useful terminal UI for Kubernetes cluster management. It makes interacting with Kubernetes clusters slightly more tolerable meaning that I can spend more time ~breaking main~ implementing features and less time ~destroying your cluster~ navigating the Kubernetes API with kubectl. [repo:derailed/k9s] ## The rub The problem with K9s' out of the box experience is that it looks the same regardless of which cluster you're connected to. This has resulted in multiple occurrences of ~wave:senior devs~ at my workplace accidentally nuking pods in production because they thought they were in testing or development. :lul: Repeated complaining about this made me think :thinker: "why doesn't each context just look different? Why can't prod be a scary red and dev be a chill blue?". ## Skins (not CS2 skins, but close enough) A feature I particularly like about K9s is the ability to [customise the appearance of the UI with skins](https://k9scli.io/topics/skins/). If we have a bit of a read of the documentation, we find the following: > If a skin file exists for your cluster then the skin will be loaded if not the stock skin remains in effect. So if your want different K9s look and feel on a per context basis and say your cluster is `clusterX` and your context is `contextY` then you skin attribute would read `skin: blee`. Where `blee` refers to a skin file in `$XDG_CONFIG_HOME/k9s/skins/blee.yaml`. Neato! This means that we can have different skins for different clusters, which is great for keeping them visually distinct and harder to accidentally destroy. [Ben Matselby](https://benmatselby.dev/post/2023/setting-up-k9s-skins-for-environments/) has a write up on what this looks like in practice, but I wanted to take it a step further and automate the process of setting up these skins and configuring K9s to use them. ## The step-by-step To make our lives easier, we should generate skins per-context and then automatically configure K9s to use the right skin for each context. I chose the following steps. ### Find a skin you like Pick a skin from [the skins directory in the K9s repo](https://github.com/derailed/k9s/tree/master/skins), and make sure it sets its background with an anchor, like this line from `one-dark.yaml`: ```yaml background: &background "#282c34" ``` Everything else in the skin that should be the background colour refers back to it as `*background`, so changing that one line recolours the whole UI. The reason why this matters will become clear in the next step. As of 10/11/2026, these skins have one: - dracula - gruvbox-dark, gruvbox-dark-hard, gruvbox-light, gruvbox-light-hard - gruvbox-material-dark-hard, -medium and -soft, and gruvbox-material-light-hard, -medium and -soft - kanagawa - modus-operandi and modus-vivendi, plus their -deuteranopia, -tinted and -tritanopia variants - monokai - nightfox - nord - one-dark, one-light - rose-pine - solarized-16, solarized-dark, solarized-light - vercel The colours I use below are dark tints, so they suit a dark skin. I chose "one-dark" for my workplace, but I think that "dracula" looks cooler. ### Create skin variations Next we create variations of the skin by replacing the background value with different colours. I use [yq](https://github.com/mikefarah/yq) to manipulate the yaml, which keeps the `&background` anchor in place. For example, to make a 'red' variation of the 'one-dark' skin: ```bash yq '.background = "#3f1f28"' "$SKINS_PATH/one-dark.yaml" \ > "$SKINS_PATH/one-dark-red.yaml" ``` `$SKINS_PATH` is the `Skins:` line of `k9s info`, which is `~/.config/k9s/skins` on Linux. ### Configure K9s to use the right skin for each context Finally, we need to tell K9s which skin to use for each context. K9s keeps per-context settings in the `Context Configs:` directory from `k9s info` (`~/.local/share/k9s/clusters` on Linux), in a folder for the cluster and then a folder for the context: `clusters///config.yaml`. For example, if the 'aks-scary-production' context points at a cluster also called 'aks-scary-production', and we want it to use the 'one-dark-red' skin, we write `clusters/aks-scary-production/aks-scary-production/config.yaml`: ```yaml k9s: skin: one-dark-red ``` The cluster comes first, so if your context and cluster names differ, check your kubeconfig for the cluster name. ## Putting it all together I wrote a bash script to automate this process, which you can find in the [repo:WillMatthews/k9s-config] repository. You describe what you want in a small YAML file: ```yaml theme: one-dark colours: red: "#3f1f28" purple: "#2f1f3f" grey: "#2f2f2f" contexts: - name: aks-bo-nd-dev colour: red - name: aks-bondtest-uk colour: purple - name: minikube colour: grey ``` and run `./k9s_configure.sh`. It downloads the skin, makes a variation for each colour, looks up each context's cluster in your kubeconfig, and writes the config files from the steps above. It only needs k9s, yq and curl, so it doesn't care how you installed them, and it's safe to run again whenever the list changes. I include this script within bootstrap scripts for projects at my workplace, so that anyone who clones the repo and runs the bootstrap script will have their K9s configured with the right skins for each cluster. Having consistency across everyone's machines is very helpful for avoiding accidents and making it easier to visually distinguish between clusters - especially when you're running over to someone else's machine to help them out, and you don't have to worry about "do they have a different theme to me?". However I must wonder if in the 'age of AI-driven code' blog posts like this even matter any more if we can conjure stuff like this up from the void. :elmo-fire: