The problem
You deployed an application to Kubernetes. Something is broken. You run kubectl get pods — the Pod is there. You run kubectl get replicasets — that is there too. But which ReplicaSet belongs to which Deployment? Which objects disappear if you delete this resource?
Without the right tool, you chase ownerReferences manually through YAML. That is slow and easy to misread, especially when several apps share a namespace or when old ReplicaSets from rollouts are still around.
Enter kubectl tree
kubectl tree is a kubectl plugin (by Ahmet Alp Balkan) that visualizes ownership between API objects as an ASCII tree. It follows the ownerReferences field on each object and shows parent → child relationships in one view.
Instead of piping five commands together, you get something like:
kubectl tree deployment/web -n shop-staging
NAMESPACE NAME READY REASON
shop-staging
└─ Deployment/web
├─ ReplicaSet/web-7f9c8d4b5
│ └─ Pod/web-7f9c8d4b5-xk2j9
└─ ReplicaSet/web-6a1b2c3d4
└─ Pod/web-6a1b2c3d4-m9p0q
The old ReplicaSet might still have a Pod if a rollout is mid-flight or if something stalled. The tree makes that visible without opening three YAML files.
What ownerReferences actually are
Kubernetes sets owners when one object creates or controls another. A Deployment owns its ReplicaSets; a ReplicaSet owns its Pods. Many higher-level objects follow the same pattern (Jobs → Pods, CronJobs → Jobs, some CRDs → child resources).
kubectl tree only shows what the API records in ownerReferences. It does not infer relationships from labels alone. If your operator links resources only via labels, the tree may look flat or incomplete — that is a clue about how the app is modeled, not a bug in the plugin.
Install the plugin (Krew)
The usual install path is Krew, the plugin manager for kubectl:
kubectl krew version
kubectl krew install tree
kubectl tree --help
If krew is not installed yet, follow the official install guide for your OS, then add the Krew path to your shell as documented there. On shared machines, prefer a user-local install over copying binaries into /usr/local/bin without telling anyone.
Basic usage
Point the plugin at any object you already identified — often a Deployment or a stuck Pod:
kubectl tree deployment/web -n shop-staging
kubectl tree pod/web-7f9c8d4b5-xk2j9 -n shop-staging
Use all namespaces when you only have a name and are not sure where it lives:
kubectl tree deployment/web -A
Filter with a label selector when the app is spread across several Deployments with common labels:
kubectl tree --selector app=web -n shop-staging
Check context and namespace first — same habit as with plain kubectl get. A perfect tree on the wrong cluster is still the wrong answer.
When I reach for it
Before a delete. “If I remove this Deployment, what else goes with it?” The tree shows owned ReplicaSets and Pods. It does not show unrelated objects that merely reference the same image or ConfigMap — those are not owners.
During rollout confusion. Multiple ReplicaSets, Pods in Terminating, or a Service pointing at labels that no longer match any ready Pod — the tree answers “what belongs to this controller?” faster than sorting kubectl get rs by hand.
Teaching and incident notes. Pasting a small tree into a ticket is often clearer than three screenshots of kubectl get.
It does not replace kubectl describe (Events, conditions), kubectl logs, or metrics. A healthy-looking tree can still hide a crashing container or a failed probe.
Limitations worth knowing
- Only owner chains. Siblings linked by labels, Services, or Ingress backends do not appear as children.
- CRDs and operators vary. Some set ownerReferences correctly; some do not. If the tree stops early, read the CRD’s child objects with
kubectl getanddescribeanyway. - Large namespaces can produce wide output. Narrow with
-n, a known root object, or--selector. - Permissions are the same as kubectl. If you cannot
geta type, tree cannot show it either.
Complement, do not replace, the usual loop
My default debugging order is unchanged: confirm context and namespace, then get, then describe, then logs. I add kubectl tree when ownership is the question — especially after Helm upgrades, GitOps syncs, or manual edits left extra ReplicaSets behind.
If you are still building kubectl habits, the kubectl essentials for beginners post on this site covers the core verbs first. Tree is a small plugin on top of that foundation.
Final thought
You do not need dozens of kubectl plugins to work in Kubernetes. kubectl tree earns its place because ownership is easy to forget and painful to reconstruct from YAML. Install it once, use it when relationships matter, and keep describing Pods when they misbehave — the tree shows structure; Events and logs still show behaviour.