kubectl logs is the first command you reach for when something breaks — and most people know only the bare version. The flags below are the difference between scrolling through a million lines and finding the error in the first ten.

Table of Contents

  • The basics
  • Follow and tail
  • Multi-container pods
  • Logs from many pods at once
  • Time filtering
  • Crashed containers: --previous
  • Debugging when logs are empty
  • Beyond kubectl logs

The basics

kubectl logs <pod-name> # current container logs kubectl logs <pod-name> -n <namespace> # non-default namespace 

Logs come from the container's stdout/stderr — if your app writes to a file instead of stdout, kubectl logs shows nothing. That's a container-design problem (log to stdout), not a kubectl problem.

Follow and tail

kubectl logs -f <pod> # stream live, like tail -f kubectl logs --tail=100 <pod> # last 100 lines only kubectl logs <pod> | tail -50 # same, via pipe 

-f is your deploy-verification workhorse: kubectl logs -f deploy/my-app right after kubectl rollout watches the new version come up.

Multi-container pods

Sidecars mean one pod, several log streams. -c picks the container:

kubectl logs <pod> -c app # one container kubectl logs <pod> --all-containers # every container, prefixed kubectl logs <pod> -c istio-proxy # the sidecar specifically 

Forgetting -c on a multi-container pod gets you a container name must be specified — or silently the wrong container's logs.

Logs from many pods at once

kubectl logs accepts a selector or a workload instead of a pod name — it streams from one matching pod, or fans out with --prefix:

kubectl logs -l app=web --all-containers --prefix # every web pod, prefixed kubectl logs deploy/api --tail=50 # one pod from the deployment kubectl logs -l app=web --max-log-requests 10 # cap concurrent streams 

For serious multi-pod tailing, stern (a tiny dedicated tool) does it better: stern web streams all matching pods with color-coded prefixes.

Time filtering

kubectl logs --since=10m <pod> # last 10 minutes kubectl logs --since=1h <pod> # last hour kubectl logs --since-time=2026-10-15T09:00:00Z <pod> # absolute RFC3339 

--since after an incident narrows the haystack before you grep.

Crashed containers: --previous

kubectl logs <pod> --previous # logs of the *last* crashed container 

This is the CrashLoopBackOff move: the current container keeps restarting, so its logs are empty or truncated — --previous gets the logs from the run that just died, which is where the actual panic/error lives. If --previous returns "container not found," the pod restarted more than once or was rescheduled — check kubectl describe for the restart count and reason.

Debugging when logs are empty

kubectl logs returning nothing is itself a signal:

  • Container never started — check kubectl describe pod events (probe failures, CreateContainerError, ImagePullBackOff — see fixing ImagePullBackOff).
  • App logs to a file — kubectl exec in and read it, or reconfigure the app to log to stdout.
  • Log rotation evicted data — kubelet keeps a bounded file; long-running pods lose early logs. For anything beyond "what just happened," you need a log pipeline.
  • Pod was deleted and recreated — logs die with the pod. --previous only works for restarts within the same pod.

Beyond kubectl logs

kubectl logs is for live debugging — it's not retention. For history across restarts, pod deletions, and multi-node searches, you want log aggregation: Grafana Loki is the lightweight Kubernetes-native option, and pairs with the alerting covered in Prometheus Alerting.

Related Articles

Author: CloudOpsGuide Team