kubectl is the command-line client for the Kubernetes API. It is a single binary with no dependencies, and it is the tool you will spend most of your Kubernetes time in.
brew install kubectlcurl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectlFor upgrades through your package manager, the official apt and yum repositories are the better route. Note they are versioned per minor release, so moving from 1.31 to 1.32 means updating the repository URL as well.
winget install -e --id Kubernetes.kubectlchoco install kubernetes-cli and scoop install kubectl work equally well.
kubectl version --clientClient Version: v1.31.2
Kustomize Version: v5.4.2
--client matters here, without it, kubectl version tries to contact a cluster and errors if none is configured.
kubectl is supported one minor version either side of the cluster's API server. A v1.31 client works with 1.30, 1.31 and 1.32.
Outside that range things mostly work until they suddenly do not, usually a missing field on a newer resource, or a subcommand the older server does not recognise.
kubectl version # shows both client and serverIf you work across clusters on different versions, asdf or kubectl version-switching tools let you keep several binaries and select per context. In practice, keeping the client at the newest cluster version you talk to covers most cases.
kubectl reads ~/.kube/config. Local cluster tools write it for you:
kind create cluster # writes and selects the context
minikube start # sameCloud CLIs add a context on request:
aws eks update-kubeconfig --region us-east-1 --name production
gcloud container clusters get-credentials production --region us-central1
az aks get-credentials --resource-group rg --name productionkubectl config get-contexts
kubectl config current-context
kubectl cluster-infokubectl cluster-info returning a URL means you are connected. connection refused usually means no cluster is running; Unauthorized means credentials, not connectivity.
Several config files can be merged with a colon-separated KUBECONFIG:
export KUBECONFIG=~/.kube/config:~/.kube/client-a.yamlThe highest-value five minutes you will spend. Completion covers resource names, not just subcommands, so you stop copying and pasting Pod names:
# bash
echo 'source <(kubectl completion bash)' >> ~/.bashrc
# zsh
echo 'source <(kubectl completion zsh)' >> ~/.zshrc
# fish
kubectl completion fish > ~/.config/fish/completions/kubectl.fishecho 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrcThe second line is what most people omit, and without it completion stops working through the alias.
Nothing in kubectl's output tells you which cluster you are on, and running a destructive command against the wrong one is the most common serious mistake people make. Show the context in your prompt, kube-ps1, or the Kubernetes segment in starship, powerlevel10k or oh-my-zsh:
[prod:payments] ~/work $
krew is kubectl's plugin manager:
kubectl krew install ctx ns tree neat| Plugin | What it does |
|---|---|
ctx | Switch contexts with fuzzy matching |
ns | Switch namespaces the same way |
tree | Show resources as an ownership tree |
neat | Strip generated fields from get -o yaml output |
kubectl neat deserves particular mention: kubectl get pod x -o yaml includes managed fields, status and defaults, and neat strips it to something you can actually read or reuse as a manifest.
Outside krew, stern for multi-Pod log tailing and k9s for a terminal UI are both worth installing.
kubectl cluster-info
kubectl get nodes
kubectl get pods -A
kubectl api-resources # every type this cluster supports, with short names
kubectl explain pod.spec # field docs from the live API serverkubectl explain is the one to remember. It documents fields against the server you are connected to, so it always matches your version rather than whatever a search result described.
| Task | Command |
|---|---|
| Client version | kubectl version --client |
| Both versions | kubectl version |
| Am I connected | kubectl cluster-info |
| List contexts | kubectl config get-contexts |
| Switch cluster | kubectl config use-context <name> |
| Default namespace | kubectl config set-context --current --namespace=<ns> |
| Completion (zsh) | source <(kubectl completion zsh) |
| Install a plugin | kubectl krew install <name> |
| Field docs | kubectl explain <resource>.<field> |
| Supported types | kubectl api-resources |
kubectl version --client avoids the error when no cluster is configured.k alias, including the complete -o default -F __start_kubectl k line.kubectl explain matches your cluster's version, unlike documentation you find by searching.krew, stern and k9s are the additions most people end up installing anyway.<MCQExercise exercise={{ id: 'k8s-kubectl-version-flag', title: 'A version check that errors', description: 'On a fresh machine with no cluster configured, kubectl version fails with a connection error. How do you check the installed version?', options: [ { id: 'a', text: 'kubectl version --client, which reports only the local binary and contacts nothing' }, { id: 'b', text: 'kubectl cluster-info, which includes the client version' }, { id: 'c', text: 'Reinstall kubectl, since a working install never requires a cluster' }, { id: 'd', text: 'kubectl config view, which records the version at install time' }, ], correctId: 'a', explanation: 'By default kubectl version reports both client and server versions, so it attempts to reach the API server and fails when none is configured. The --client flag limits it to the local binary. This is a common first-run confusion. The installation is fine, the command is simply asking a question that needs a cluster.', wrongExplanations: { b: 'cluster-info exists purely to query a cluster, so it fails for the same reason and reports no client version at all.', c: 'The binary is working correctly. Reinstalling changes nothing about a command that needs a server to answer fully.', d: 'config view prints kubeconfig contents, which contain clusters, users and contexts, never the kubectl version.', }, }} />
<MCQExercise exercise={{ id: 'k8s-kubectl-alias-completion', title: 'Completion that stops at the alias', description: 'A developer adds alias k=kubectl and sources kubectl completion. Completion works for kubectl but not for k. What is missing?', options: [ { id: 'a', text: 'complete -o default -F __start_kubectl k, which registers the completion function against the alias' }, { id: 'b', text: 'The alias must be defined before the completion script is sourced' }, { id: 'c', text: 'Aliases cannot support completion in bash or zsh' }, { id: 'd', text: 'kubectl completion must be regenerated with a --alias flag' }, ], correctId: 'a', explanation: 'The completion script registers its function for the name kubectl only. The shell treats k as a separate command with no completion attached, so you get nothing. One extra line binds the same function to the alias, and it is the step most people miss when setting this up.', wrongExplanations: { b: 'Ordering does not matter, because the completion script never inspects aliases. The binding has to be declared explicitly either way.', c: 'Aliases support completion perfectly well once a completion function is registered for them. That is exactly what the extra line does.', d: 'kubectl completion takes a shell name, not an alias. The alias binding is a shell-level concern rather than a kubectl one.', }, }} />
Not exactly. kubectl is supported within one minor version either side of the API server, so a v1.31 client works with clusters running 1.30, 1.31 or 1.32. Beyond that, most things still work until a newer resource field or subcommand is missing, at which point failures look confusing. kubectl version shows both client and server so you can check the gap. If you manage clusters on different versions, keep the client at the newest version you talk to.
Because it reports both the client and the server version by default, so it tries to reach a cluster. With no kubeconfig, or no cluster running, that fails. kubectl version --client reports only the local binary and contacts nothing. This trips up almost everyone immediately after installing, and does not indicate a problem with the installation.
Local tools handle it for you, kind create cluster and minikube start both write and select a context in ~/.kube/config. For cloud clusters, run the provider's command: aws eks update-kubeconfig, gcloud container clusters get-credentials, or az aks get-credentials. Verify with kubectl cluster-info, which prints the API server URL when connected. A connection refused means no cluster is reachable; Unauthorized means the credentials are the problem rather than the connection.
Source the completion script from your shell profile: echo 'source <(kubectl completion zsh)' >> ~/.zshrc for zsh, or the bash equivalent. It completes resource names as well as subcommands, so you stop copying and pasting Pod names. If you also alias k=kubectl, add complete -o default -F __start_kubectl k, without that line completion works for kubectl but not through the alias, which is the step most people miss.
Install krew first, then ctx and ns for fast context and namespace switching, tree for viewing resource ownership hierarchies, and neat for stripping managed fields and status out of get -o yaml so the output is readable. Outside krew, stern handles multi-Pod log tailing far better than kubectl logs -l, and k9s gives you a terminal UI that makes browsing a cluster much quicker than repeated get and describe commands.
Learn the kubectl commands you will run daily: get, apply, describe, delete, selectors, output formats, namespaces and tips to work faster with kubeconfig.
Inspect cluster resources with kubectl get and describe. Use -o wide, jsonpath and custom-columns, read events, and know when to choose get over describe.
Learn kubectl apply vs create vs replace, preview changes with kubectl diff, use server-side apply, delete resources safely and fix objects stuck deleting.