stern: Tail Pod Logs by Regex in CI
stern <query> streams logs from every pod whose name matches the regex, prefixing each line with the pod and container.
kubectl logs handles one pod; stern follows a whole Deployment or ReplicaSet by name pattern. In CI it surfaces logs from all replicas while an integration test runs.
What it does
stern takes a pod query (a regex, or a resource/name like deployment/api) and tails the logs of every matching pod and container at once. Each line is prefixed and colored by pod, so interleaved output stays readable.
Common usage
stern api # all pods whose name matches /api/
stern "web-.*" --since 5m # only the last 5 minutes
stern deployment/checkout # follow a Deployment's pods
stern api -c nginx # only the nginx containerOptions
| Flag | What it does |
|---|---|
| <query> | Pod name regex, or kind/name (e.g. deployment/api) |
| -n, --namespace | Namespace to watch (default: current context) |
| -c, --container | Container name regex within each pod |
| --since | Only logs newer than a duration, e.g. 10m, 1h |
| --tail | Number of prior lines per container (-1 = all) |
| -o, --output | Output format: default, raw, json, ppextjson |
In CI
Run stern in the background with --no-follow --since 2m to dump recent logs once and exit, instead of streaming forever. Use -o raw when you pipe into a log assertion, since the default prefix breaks line-exact grep.
Common errors in CI
no pods found for the given query means the regex matched nothing in that namespace; check -n and that the workload exists. failed to set up watch: ... Unauthorized means the kubeconfig token expired or the ServiceAccount lacks pods/log. On large clusters --all-namespaces plus a broad regex can hit too many open files; narrow the query.
Using this in CI
A runner has no kubeconfig, no cached context, and no interactive auth. Every kubectl invocation in CI needs the context supplied explicitly, and most confusing CI failures here are the command running against the wrong cluster or no cluster at all.
# never rely on the ambient context on a runner
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pods
# confirm what you are actually connected to before mutating anything
kubectl config current-context
kubectl cluster-info
# fail fast instead of hanging on an unreachable API server
kubectl --request-timeout=30s get nodes