KubeGlanceDownload
How KUBECONFIG merges multiple files (first wins, not last)

kubectl

How KUBECONFIG merges multiple files (first wins, not last)

Set KUBECONFIG to several files and kubectl merges them by a rule that surprises most people: the first file to define a name wins, and later files are ignored.

· 8 min read

You can point KUBECONFIG at several files at once and kubectl will treat them as one configuration:

export KUBECONFIG=~/.kube/config:~/.kube/work.yaml:~/.kube/prod.yaml

Most people assume this behaves like PATH-style overriding, where the last definition wins. It is the opposite. The first file to define a given name wins outright, and every later definition of that name is discarded silently.

That single inversion is behind almost every “why am I connected to the wrong cluster” story.

How kubectl loads your config

Before the merge rules make sense, it helps to see what the client actually does. There are two stages people collapse into one: choosing which files to read, then merging them.

The loader walks the files in order and claims each name the first time it sees it. Later files can only add, never replace.

The merge is not a deep merge and it is not last-write-wins. Each named entry is claimed once, and the claim is permanent for that invocation.

The rules, precisely

Files are read left to right. First definition of a name wins; later ones are discarded without a warning.

Stated as prose, because it is worth being exact:

  1. Files are read left to right, separated by : (; on Windows).
  2. Merging happens per named entry, independently for clusters, users and contexts. The first file to define the name production as a cluster wins; a different file can still win for the user named production.
  3. current-context comes from the first file that sets a non-empty one. Later files cannot change it.
  4. Empty or missing files in the list are skipped without complaint.
  5. Relative paths inside a kubeconfig resolve against that file’s own directory, not your working directory. A certificate-authority: ca.crt in ~/.kube/work/config means ~/.kube/work/ca.crt, regardless of where you run kubectl from.

Rule 5 is the one that breaks when people reorganise their ~/.kube folder, and the error it produces — a TLS failure — points nowhere near the cause.

The collision that actually bites you

Cluster provisioners are lazy about naming. kind names its cluster entries kind-<name>, which is fine, but plenty of tools do not bother:

  • minikube writes a cluster and user both called minikube
  • Many on-prem and kubeadm setups write a cluster called kubernetes and a user called kubernetes-admin
  • Hand-written configs love default

Put two of those in KUBECONFIG and one of them simply disappears. The context name from the second file may still exist — contexts and clusters are merged separately — so you get a context called staging that points at a cluster entry called kubernetes which was defined by the first file, meaning it points at production.

Everything looks correct. kubectl config get-contexts shows staging. You are talking to the wrong cluster.

Check for it directly:

kubectl config view -o jsonpath='{range .contexts[*]}{.name}{"\t→\t"}{.context.cluster}{"\n"}{end}'

Then confirm the server URL behind the cluster your context names:

kubectl config view -o jsonpath='{.clusters[?(@.name=="kubernetes")].cluster.server}'

If the URL is not the one you expect, you have a collision. Renaming is the only real fix: kubectl config rename-context handles contexts, but cluster and user names have to be edited in the file.

See what the merge produced

kubectl config view always shows the merged result, never a single file. That makes it the tool for answering “what does kubectl actually think right now”:

kubectl config view                 # merged, credentials redacted
kubectl config view --raw           # merged, credentials included — careful
kubectl config view --minify        # only the current context and what it uses
kubectl config get-contexts         # tabular, marks the current one with *

--minify combined with --flatten is the safest way to hand one cluster’s access to someone without leaking the other nineteen:

kubectl config view --minify --flatten > handover.yaml

What --flatten is for

--flatten inlines every file reference — certificates, keys, token files — as base64 *-data fields, producing a config that is self-contained and portable. Without it, you send a file full of paths to certificates that only exist on your laptop, which is the usual reason a shared kubeconfig fails immediately for the person you sent it to.

To permanently combine several files into one:

KUBECONFIG=~/.kube/config:~/.kube/work.yaml kubectl config view --flatten > ~/.kube/merged.yaml

Back up the originals first. And remember the merge rule applies here too — if the files collide, the merged output silently contains only the winners.

Where do writes go?

kubectl config set-context, use-context and friends have to pick a file to modify. The behaviour with multiple files:

  • If the entry being modified already exists in one of the files, that file is updated in place.
  • If it is new, it is written to the first file in the list.

So kubectl config use-context prod changes current-context in your first file, which is usually ~/.kube/config. That is also why a use-context sometimes appears to do nothing: you set it in the first file, but a different first file was already supplying current-context in the session where you checked.

--kubeconfig turns merging off

The --kubeconfig flag does not add a file to the chain — it replaces the chain entirely. kubectl --kubeconfig ~/.kube/prod.yaml get pods sees that file and nothing else, ignoring KUBECONFIG and ~/.kube/config completely.

This makes it the reliable way to run something against exactly one cluster without wondering what else is in scope. It is a good habit for anything destructive, and a good habit in scripts, where an inherited KUBECONFIG from the calling shell is a genuine hazard.

The full precedence chain, highest first:

  1. --kubeconfig flag — used alone, no merging
  2. KUBECONFIG environment variable — merged left to right
  3. ~/.kube/config

A saner setup than one giant file

The pattern that holds up over time is one file per cluster, named after the cluster, never merged into a single blob:

~/.kube/
  config          # personal / local only
  work-eu.yaml
  work-us.yaml
  prod.yaml

Then select rather than merge:

alias kprod='kubectl --kubeconfig ~/.kube/prod.yaml'

You give up cross-cluster context switching, and you gain the guarantee that a command can only ever reach one cluster. For production access that is a trade worth making — and it eliminates name collisions entirely, because nothing is being merged.

Why we care about this exactly

KubeGlance reads your kubeconfig and nothing else — no agent in the cluster, no account, no telemetry — so it has to implement these merge semantics identically to kubectl. If the app resolved a context to a different cluster than kubectl would, it would be showing you a different reality than your terminal, which is a worse-than-useless dashboard.

It also picks up the sibling files sitting in ~/.kube/ that are not in your KUBECONFIG, because in practice that is where most people’s other clusters live — and offers each one as an importable source rather than merging them behind your back. Names that would have collided stay distinct, and you keep the per-file separation described above.

If you want to understand more of what kubectl computes rather than fetches, the STATUS column is the other big one: how kubectl decides what to put in the STATUS column. And when a context is right but the connection still fails, the cause is often credentials rather than config — scoping a read-only kubeconfig covers what a kubeconfig actually grants.

Several clusters, several kubeconfig files? KubeGlance imports each one as a separate source and keeps them distinct — merged into one live view on Mac, iPhone and iPad.

Get KubeGlance
#kubeconfig #kubectl #contexts #multi-cluster

The Kubernetes dashboard that fits in your pocket

KubeGlance is a native Kubernetes client for iPhone and iPad — the real dashboard, not a companion — with a full Mac app on the same core. Pods, workloads, logs and events, straight from your kubeconfig.

Download KubeGlance