-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 - --- 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: -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEJtHIQX7gIiqRWdSs/RuAbpOsR8kFAmryqSUACgkQ/RuAbpOs R8lc5hAAhV0OhT6wYy6LS/r/ASnMSFWqBkNAlJpie5yE3zDrI3cHHAkk4Ur0I0pS aDzbb9Gh4LbPbonFueO+nALBJS8Qz3VCjUonN5b36J3nb9508EC7ROM80p2dROaT R+OhDFjhmCHPiCG2n1lSD8BL8r3p78a1+y5wrYHJKEA/aPfyRtFKR/uWgRPk2BG0 gc33qzNY8X3MEXR68yPArctVk4zgNn+cW3Y0Qb7tw7by/h1NrLUwT8E5RW7CpjqP AaWWc/VPi6cwScbI98Zm/2ff1kBZPJr5jM9Ftq3qub+29D0xAKcuefw491Yr6YTe H7GSDMSAd49UYKwmsQmLS2pXvK//PUv5KO1U6wzkU8AOuyfohLPZqXI5Sh2xHs22 91ZOzNkOVaGewICn6jemaHgwkU3b0rE16D3M2zZSw7o4Sity+4zdm6kRTu+kyRto 8H27DgZFspUUuWrp1BxSKDBiVCn04MarG/IwVD4UQX9wWMAqsADhJWlwSIsKd62a 5UmozMgbVLoZyW5Ocpba3RetMlzTJxjsGIdRnU2vQOP9HLlOBgmuzVw31J7nWz0n AcMDGTztk3FmFC7eXmOkF2Sz2vGuFXSTjOaCufqEHUqh0uD0KWQh5WAgVZETGmkk L0fnfsTCZxskWqhmZKhSY1rv+vofGmsx3Ff3J1KqHTObAEX3QVI= =SPZl -----END PGP SIGNATURE-----