NexusCS

kubectl

Kubernetes
Command-line tool for Kubernetes. Deploy apps, inspect resources, debug pods, view logs, and manage clusters. Complete reference for kubectl commands, troubleshooting workflows, and common error fixes.
kubernetes
kubectl
k8s
cli
devops

Getting started

Introduction

kubectl is the command-line tool for Kubernetes cluster management. Use it to deploy applications, inspect resources, view logs, and troubleshoot workloads.

Installation

# macOS (Homebrew)
brew install kubectl

# Linux (binary)
curl -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/kubectl

# Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y kubectl

Shell completion

# Bash
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc

# Zsh
source <(kubectl completion zsh)
echo "source <(kubectl completion zsh)" >> ~/.zshrc

# Alias with completion
alias k=kubectl
complete -o default -F __start_kubectl k

First commands

# Check cluster connection
kubectl cluster-info

# View current context
kubectl config current-context

# Get all pods
kubectl get pods

# Get pods in all namespaces
kubectl get pods --all-namespaces

Context & Configuration

Context management

# Show current context
kubectl config current-context

# List all contexts
kubectl config get-contexts

# Switch context
kubectl config use-context CONTEXT_NAME

# Rename context
kubectl config rename-context OLD NEW

# Delete context
kubectl config delete-context CONTEXT_NAME

Namespace management

# Set default namespace
kubectl config set-context --current --namespace=dev

# List namespaces
kubectl get namespace

# Create namespace
kubectl create namespace dev

# Delete namespace
kubectl delete namespace dev

Kubeconfig

# View merged config
kubectl config view

# View with credentials
kubectl config view --raw

# Current context only
kubectl config view --minify

# Multiple configs
export KUBECONFIG=~/.kube/config:~/.kube/config2

Core Commands

Get resources

# Basic get
kubectl get pods

# More details
kubectl get pods -o wide

# YAML format
kubectl get pods -o yaml

# JSON format
kubectl get pods -o json

# Label selector
kubectl get pods --selector=app=myapp

# Field selector
kubectl get pods --field-selector=status.phase=Running

# Sort by timestamp
kubectl get pods --sort-by=.metadata.creationTimestamp

# All namespaces
kubectl get pods --all-namespaces

# Specific namespace
kubectl get pods -n NAMESPACE

Describe resources

# Describe pod
kubectl describe pod POD_NAME

# Describe node
kubectl describe node NODE_NAME

# Describe deployment
kubectl describe deployment DEPLOY_NAME

Create resources

# From file
kubectl create -f manifest.yaml

# Deployment
kubectl create deployment nginx --image=nginx

# Namespace
kubectl create namespace dev

# Secret
kubectl create secret generic SECRET_NAME \
  --from-literal=key=value

# ConfigMap
kubectl create configmap CONFIG_NAME \
  --from-file=config.txt

Apply (declarative)

# Apply file
kubectl apply -f manifest.yaml

# Apply directory
kubectl apply -f directory/

# Kustomize
kubectl apply -k kustomization/

# Server-side dry run
kubectl apply -f manifest.yaml --dry-run=server

# Client-side dry run
kubectl apply -f manifest.yaml --dry-run=client

Delete resources

# Delete pod
kubectl delete pod POD_NAME

# Delete from file
kubectl delete -f manifest.yaml

# Delete all pods
kubectl delete pods --all

# Delete by label
kubectl delete pods --selector=app=myapp

# Force delete
kubectl delete pod POD_NAME --force --grace-period=0

Edit resources

# Edit pod
kubectl edit pod POD_NAME

# Edit deployment
kubectl edit deployment DEPLOY_NAME

Patch resources

# Strategic merge patch
kubectl patch pod POD_NAME \
  -p '{"spec":{"containers":[{"name":"app","image":"nginx:1.21"}]}}'

# JSON patch
kubectl patch deployment DEPLOY_NAME --type='json' \
  -p='[{"op": "replace", "path": "/spec/replicas", "value":3}]'

# Merge patch
kubectl patch deployment DEPLOY_NAME --type='merge' \
  -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","image":"nginx:1.22"}]}}}}'

Debugging & Logs

Logs

# Basic logs
kubectl logs POD_NAME

# Specific container
kubectl logs POD_NAME -c CONTAINER_NAME

# Follow logs
kubectl logs -f POD_NAME

# Previous container
kubectl logs --previous POD_NAME

# With timestamps
kubectl logs --timestamps POD_NAME

# Last 20 lines
kubectl logs --tail=20 POD_NAME

# Last hour
kubectl logs --since=1h POD_NAME

# Since timestamp
kubectl logs --since-time=2026-02-04T10:00:00Z POD_NAME

# By label
kubectl logs -l app=myapp --all-containers=true

Exec & Attach

# Execute command
kubectl exec POD_NAME -- ls /app

# Specific container
kubectl exec POD_NAME -c CONTAINER_NAME -- env

# Interactive shell (bash)
kubectl exec -it POD_NAME -- /bin/bash

# Interactive shell (sh)
kubectl exec -it POD_NAME -- /bin/sh

# Attach to process
kubectl attach POD_NAME -it

Port forward

# Forward pod port
kubectl port-forward POD_NAME 8080:80

# Forward service port
kubectl port-forward svc/SERVICE_NAME 8080:80

# Forward deployment port
kubectl port-forward deployment/DEPLOY_NAME 8080:80

Copy files

# Copy from pod to local
kubectl cp NAMESPACE/POD_NAME:/path/to/file /local/path

# Copy from local to pod
kubectl cp /local/path NAMESPACE/POD_NAME:/path/to/file

# Specific container
kubectl cp NAMESPACE/POD_NAME:/path/to/file /local/path \
  -c CONTAINER_NAME

Resource usage

# Pod CPU/memory
kubectl top pod

# Specific pod
kubectl top pod POD_NAME

# Per-container
kubectl top pod --containers

# Node resources
kubectl top node

# Specific node
kubectl top node NODE_NAME

Debug (ephemeral containers)

# Add debug container
kubectl debug POD_NAME -it --image=busybox \
  --target=CONTAINER_NAME

# Create debug copy
kubectl debug POD_NAME -it --copy-to=POD_NAME-debug \
  --container=debug --image=busybox

# Debug node
kubectl debug node/NODE_NAME -it --image=ubuntu

Pod Lifecycle

Pod phases

Phase Description
Pending Pod accepted but container(s) not created yet
Running Pod bound to node, at least one container running
Succeeded All containers terminated successfully
Failed All containers terminated, at least one failed
Unknown Pod state cannot be determined

Common error states

State Description
CrashLoopBackOff Container repeatedly crashing
ImagePullBackOff Cannot pull container image
ErrImagePull Failed to pull image
CreateContainerConfigError ConfigMap/Secret missing
InvalidImageName Image name syntax error
ImageInspectError Cannot inspect image

Troubleshooting

CrashLoopBackOff diagnosis

# 1. Check pod status
kubectl get pod POD_NAME
kubectl describe pod POD_NAME

# 2. Check current logs
kubectl logs POD_NAME

# 3. Check previous logs
kubectl logs --previous POD_NAME

# 4. Check events
kubectl get events --field-selector involvedObject.name=POD_NAME

# 5. Check resource limits
kubectl describe pod POD_NAME | grep -A 5 "Limits\|Requests"

Check logs for application errors, then verify resource limits aren't causing OOM kills. If the container exits immediately, check the entrypoint command.

ImagePullBackOff diagnosis

# 1. Describe pod for error details
kubectl describe pod POD_NAME

# 2. Check image name
kubectl get pod POD_NAME -o jsonpath='{.spec.containers[*].image}'

# 3. Check image pull secrets
kubectl get pod POD_NAME -o jsonpath='{.spec.imagePullSecrets}'
kubectl get secret SECRET_NAME -o yaml

# 4. Test image pull manually
kubectl run test --image=IMAGE_NAME --dry-run=client -o yaml

Verify the image tag exists in the registry and check if authentication is required. Private registries need imagePullSecrets configured.

Pending pod diagnosis

# 1. Check pod status and events
kubectl describe pod POD_NAME

# 2. Check node resources
kubectl top nodes
kubectl describe nodes

# 3. Check taints and tolerations
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints

# 4. Check pod scheduling constraints
kubectl get pod POD_NAME -o yaml | \
  grep -A 10 "nodeSelector\|affinity\|tolerations"

# 5. Check PVC binding
kubectl get pvc

Look for Insufficient cpu, Insufficient memory, or taints preventing scheduling. Ensure PersistentVolumeClaims are bound.

Service not accessible

# 1. Check service endpoints
kubectl get endpoints SERVICE_NAME
kubectl describe service SERVICE_NAME

# 2. Check pod labels match selector
kubectl get pod POD_NAME -o jsonpath='{.metadata.labels}'
kubectl get service SERVICE_NAME -o jsonpath='{.spec.selector}'

# 3. Test DNS resolution
kubectl run test-dns --image=busybox --rm -it -- \
  nslookup SERVICE_NAME

# 4. Check network policies
kubectl get networkpolicies

Verify service selector matches pod labels. Empty endpoints mean no pods match the selector.

Workload Management

Deployments

# Create deployment
kubectl create deployment nginx \
  --image=nginx --replicas=3

# List deployments
kubectl get deployments

# Describe deployment
kubectl describe deployment DEPLOY_NAME

# Update image
kubectl set image deployment/DEPLOY_NAME \
  container=nginx:1.21

# Check rollout status
kubectl rollout status deployment/DEPLOY_NAME

StatefulSets

# List StatefulSets
kubectl get statefulsets

# Describe StatefulSet
kubectl describe statefulset STS_NAME

# Scale StatefulSet
kubectl scale statefulset STS_NAME --replicas=5

# Delete (keep pods)
kubectl delete statefulset STS_NAME --cascade=orphan

DaemonSets

# List DaemonSets
kubectl get daemonsets

# Describe DaemonSet
kubectl describe daemonset DS_NAME

Jobs

# Create job
kubectl create job test --image=busybox -- echo "Hello"

# List jobs
kubectl get jobs

# Describe job
kubectl describe job JOB_NAME

# Delete job
kubectl delete job JOB_NAME

CronJobs

# Create CronJob
kubectl create cronjob backup \
  --schedule="0 2 * * *" \
  --image=backup:latest -- /backup.sh

# List CronJobs
kubectl get cronjobs

# Describe CronJob
kubectl describe cronjob CRONJOB_NAME

Advanced Features

Output formats

# Wide output
kubectl get pods -o wide

# YAML output
kubectl get pods -o yaml

# JSON output
kubectl get pods -o json

# Name only
kubectl get pods -o name

# Custom columns
kubectl get pods -o custom-columns=\
NAME:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName

# JSONPath
kubectl get pods -o jsonpath='{.items[*].metadata.name}'

# JSONPath with formatting
kubectl get pods -o jsonpath=\
'{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}'

Wait for conditions

# Wait for pod ready
kubectl wait --for=condition=ready pod/POD_NAME --timeout=60s

# Wait for deployment
kubectl wait --for=condition=available \
  deployment/DEPLOY_NAME --timeout=300s

# Wait for job completion
kubectl wait --for=condition=complete \
  job/JOB_NAME --timeout=600s

# Wait for deletion
kubectl wait --for=delete pod/POD_NAME --timeout=60s

Rollout management

# Check rollout status
kubectl rollout status deployment/DEPLOY_NAME

# Rollout history
kubectl rollout history deployment/DEPLOY_NAME

# History for revision
kubectl rollout history deployment/DEPLOY_NAME --revision=2

# Undo rollout
kubectl rollout undo deployment/DEPLOY_NAME

# Undo to specific revision
kubectl rollout undo deployment/DEPLOY_NAME --to-revision=2

# Restart deployment
kubectl rollout restart deployment/DEPLOY_NAME

# Pause rollout
kubectl rollout pause deployment/DEPLOY_NAME

# Resume rollout
kubectl rollout resume deployment/DEPLOY_NAME

Scaling

# Manual scaling
kubectl scale deployment DEPLOY_NAME --replicas=3

# Scale StatefulSet
kubectl scale statefulset STS_NAME --replicas=5

# Scale down to zero
kubectl scale --replicas=0 deployment/DEPLOY_NAME

# Autoscaling (HPA)
kubectl autoscale deployment DEPLOY_NAME \
  --min=2 --max=10 --cpu-percent=80

# Get HPA status
kubectl get hpa

Labels & Annotations

# Add label
kubectl label pod POD_NAME environment=production

# Remove label
kubectl label pod POD_NAME environment-

# Add annotation
kubectl annotate pod POD_NAME description="My app pod"

# Overwrite label
kubectl label pod POD_NAME environment=staging --overwrite

Node Management

Taints

# Add taint
kubectl taint node NODE_NAME key=value:NoSchedule

# Remove taint
kubectl taint node NODE_NAME key=value:NoSchedule-

# Taint with NoExecute effect
kubectl taint node NODE_NAME dedicated=gpu:NoExecute

Taint effects: NoSchedule (prevent scheduling), PreferNoSchedule (soft constraint), NoExecute (evict existing pods)

Node operations

# Cordon (mark unschedulable)
kubectl cordon NODE_NAME

# Uncordon (mark schedulable)
kubectl uncordon NODE_NAME

# Drain (evict pods)
kubectl drain NODE_NAME --ignore-daemonsets --delete-emptydir-data

# Drain with grace period
kubectl drain NODE_NAME --force --grace-period=30

Cordon prevents new pods from scheduling. Drain evicts existing pods for maintenance.

Useful Patterns

Generate YAML with dry-run

# Generate deployment YAML
kubectl create deployment nginx --image=nginx \
  --dry-run=client -o yaml > deployment.yaml

# Generate service YAML
kubectl expose deployment nginx --port=80 \
  --dry-run=client -o yaml > service.yaml

# Generate pod YAML
kubectl run test --image=busybox \
  --dry-run=client -o yaml -- sleep 3600

Use --dry-run=client for fast YAML generation without server validation. Use --dry-run=server for validation.

Bulk operations

# Delete all pods with label
kubectl delete pods -l app=myapp

# Delete all evicted pods
kubectl get pods --all-namespaces \
  --field-selector=status.phase=Failed -o json | \
  jq -r '.items[] | "\(.metadata.namespace) \(.metadata.name)"' | \
  xargs -n 2 kubectl delete pod -n

# Restart all deployments
kubectl rollout restart deployment --all

Watch resources

# Watch pods
kubectl get pods -w

# Watch events
kubectl get events -w

# Watch with details
kubectl get pods -w -o wide

Combining with jq

# Get pod IPs
kubectl get pods -o json | jq -r '.items[].status.podIP'

# Get container images
kubectl get pods -o json | \
  jq -r '.items[].spec.containers[].image' | sort -u

# Get failed pods
kubectl get pods -o json | \
  jq -r '.items[] | select(.status.phase=="Failed") | .metadata.name'

Quirks & Gotchas

Force deletion caution

kubectl delete pod POD_NAME --force --grace-period=0

Avoid force deletion unless necessary. It bypasses graceful shutdown and can leave orphaned resources or cause data loss.

Output format performance

  • -o json and -o yaml can be slow for large resources
  • Use -o name for simple lists when you only need names
  • Use custom columns (-o custom-columns=...) for specific fields
  • JSONPath is faster than parsing full YAML/JSON

Context confusion

Always verify your current context before running destructive commands:

kubectl config current-context

Use the namespace flag to avoid accidents:

kubectl -n NAMESPACE delete pod POD_NAME

Set a default namespace in your context to avoid repetition:

kubectl config set-context --current --namespace=dev

Dry-run differences

  • --dry-run=client: Client-side validation only (faster, less accurate)
  • --dry-run=server: Server-side validation (slower, more accurate, checks admission controllers)

Use --dry-run=server before applying critical changes to catch validation errors.

Also see