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 jsonand-o yamlcan be slow for large resources- Use
-o namefor 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
- Kubernetes Official kubectl Cheatsheet - Official reference
- kubectl Installation Guide (Linux) - Linux installation
- kubectl Installation Guide (macOS) - macOS installation
- Debugging Applications - Troubleshooting guide
- Pod Lifecycle - Pod states and phases
- kubectl logs Reference - Logging command reference
- kubectl exec Reference - Exec command reference