▶Prefer to watch? This short video runs this page's main commands in a real lab, with on-screen captions and no voiceover. Click to play, or watch it on YouTube.
popeye [flags]
popeye -n my-namespace
Popeye is a read-only scanner for Kubernetes clusters. It looks at what is actually
deployed and reports misconfigurations, risky settings and leftover clutter: missing
probes, images tagged "latest", pods without resource limits, unused ConfigMaps, services
that point at nothing, and more. It then gives your cluster a score from 0 to 100 and a
letter grade. Popeye never changes anything in the cluster. It only reads.
It comes from the same author as k9s (github.com/derailed/popeye).
Install
brew install derailed/popeye/popeye # macOS and Linux (Homebrew)
go install github.com/derailed/popeye@latest # from source (needs Go)
Or download a release binary from github.com/derailed/popeye/releases, unpack it,
and put it on your PATH:
tar -xzf popeye_Linux_x86_64.tar.gz
sudo mv popeye /usr/local/bin/
Run it as a container (no install):
docker run --rm -it -v $HOME/.kube:/root/.kube derailed/popeye popeye
(The image name and tag can change; check the project's README for the current one.)
Check it works:
popeye version
Popeye uses your kubeconfig, like kubectl, and can only see what your account may read.
Test the connection first: kubectl get nodes
Run your first scan
popeye # scan the current context and namespace, all sections
popeye -A # all namespaces (--all-namespaces)
popeye -n kube-system # one namespace
popeye --context prod-cluster # a specific context
popeye --kubeconfig ~/.kube/other # a specific kubeconfig
popeye --help # every option in your version
The scan takes seconds to a minute, then prints a report and a score.
Choose what to scan (sections)
popeye -s pods # only pods
popeye -s pods,deployments,services # several sections (comma separated)
popeye -s nodes # cluster nodes: capacity, conditions, utilization
popeye -s namespaces
popeye -s pods -n default # a section in a namespace
Sections (linters) include: clusterroles, clusterrolebindings, configmaps, cronjobs, daemonsets,
deployments, horizontalpodautoscalers, ingresses, jobs, namespaces, networkpolicies, nodes,
persistentvolumes, persistentvolumeclaims, poddisruptionbudgets, pods, replicasets, roles,
rolebindings, secrets, serviceaccounts, services, statefulsets.
List what your version supports: popeye --help
Output formats
popeye -o standard # the default colorful report
popeye -o jurassic # plain text, no colors or emoji
popeye -o yaml
popeye -o json
popeye -o html # a web page report
popeye -o junit # for CI test dashboards
popeye -o score # just the score number
popeye -o prometheus # metrics format
popeye -o json --save # save the report to a file
popeye -o html --save --output-file report.html # choose the file name
popeye -o json | jq '.popeye.score' # pull a value out in a script
Filter by severity (lint level)
popeye -l ok # show everything, including passing checks
popeye -l info # informational and up
popeye -l warn # warnings and errors only
popeye -l error # errors only
Combine with a section: popeye -s pods -l warn
Read the report
GENERAL [POPEYE]
...
PODS (12 SCANNED) 💥 1 😱 3 🔊 2 ✅ 6 88%
· default/nginx-7f4c...............................................😱
🔊 [POP-206] No PodDisruptionBudget defined.
😱 [POP-102] No probes defined.
😱 [POP-106] No resources requests/limits defined.
· default/api-5d9b................................................✅
SUMMARY
Your cluster score: 88 -- B
Symbols and levels
✅ OK Passed the check
🔊 Info A suggestion worth reading
😱 Warning Should be fixed
💥 Error Needs attention now
The percent next to a section is its score. The final letter runs from A (best) down to F.
Each finding has a code like POP-102. The code tells you exactly which check fired.
Common findings and what to do
POP-100 Untagged image Give the image an explicit tag
POP-101 Image tagged "latest" Pin a version so deployments and rollbacks are repeatable
POP-102 No probes defined Add liveness and readiness probes
POP-106 No resource requests/limits Set requests and limits so one pod cannot starve the node
Unused ConfigMap / Secret / ServiceAccount Delete it, or find why nothing references it
Service with no endpoints The selector matches no pods, or the pods are not ready
Pod using the "default" ServiceAccount Create a dedicated ServiceAccount with least privilege
Container running as root / allows privilege escalation Set a securityContext
Pod uses far more or less CPU/memory than requested Right-size the requests and limits
Node under pressure or near capacity Add capacity or move workloads
PVC not bound / volume unused Check the storage class, or clean it up
Missing NetworkPolicy Add one if you need to restrict pod traffic
Missing PodDisruptionBudget Add one for important replicated apps
Check the docs for the full code list. Codes and checks change between Popeye versions.
Resource usage checks
Popeye compares what pods request with what they actually use. That needs metrics-server
running in the cluster (kubectl top pods must work). Without it, utilization checks are skipped.
Configuration file (the "spinach" file)
Popeye's checks can be tuned. Save a file, for example spinach.yml, and pass it with -f:
popeye -f spinach.yml
popeye -f spinach.yml -A -o html --save
Example spinach.yml:
popeye:
excludes:
global:
fqns:
- rx:^kube-system/ # ignore everything in kube-system
linters:
pods:
instances:
- fqns:
- rx:^default/legacy- # ignore matching pods
codes:
- 102 # ...only for this check
configmaps:
instances:
- fqns:
- rx:^default/ignore-me
Rules: fqns are "namespace/name" strings; the rx: prefix means regular expression.
Exclude things you have consciously accepted, so the report only shows what needs action.
Run popeye --help to see the exact config file option for your version, and read the
sample spinach file in the project's README for thresholds and node settings.
Use it in CI or on a schedule
popeye -A -o junit --save --output-file popeye.xml # publish as a test report
popeye -A -o json --save --output-file popeye.json
popeye -A --min-score 80 # exit with an error if the score is below 80
popeye -A --force-exit-zero # always exit 0 (report only, never fail the job)
A simple nightly job in your pipeline:
popeye -A -o html --save --output-file popeye-$(date +%F).html
It can also run inside the cluster as a CronJob, with a ServiceAccount that has read-only access
(get/list/watch on the resources it scans), and push results to S3 or Prometheus. Check the
project docs for the current manifests.
Practical one-liners
# The whole cluster, colorful report
popeye -A
# Only warnings and errors, so it is easy to read
popeye -A -l warn
# Just the pods in one namespace
popeye -s pods -n production
# Quick health number
popeye -A -o score
# Save an HTML report to share
popeye -A -o html --save --output-file cluster-report.html
# Compare two contexts
popeye --context staging -A -o score
popeye --context prod -A -o score
# List failing pods only (JSON + jq)
popeye -s pods -A -o json | jq '.popeye.sanitizers[].tally'
# Get a plain-text report with no colors for a log file
popeye -A -o jurassic > popeye.txt
# Check a namespace before and after a change
popeye -n app -o score
# Everything except system namespaces (use an exclude in the spinach file)
popeye -A -f spinach.yml
Popeye vs other tools
popeye Finds bad configuration and clutter in a running cluster (read-only)
k9s Live terminal dashboard; also has a :popeye view
kubectl The exact, scriptable commands to inspect and change things
kube-score Checks manifests (YAML files) BEFORE deployment
kubeconform Validates manifests against the Kubernetes schemas
kube-bench Checks nodes against the CIS Kubernetes security benchmark
kubescape / trivy Security and vulnerability scanning
Used together: kube-score/kubeconform in CI, Popeye on the live cluster.
Common problems
"Unable to connect" / "connection refused" Fix your kubeconfig or VPN. Test with kubectl get nodes
"Forbidden" / permission errors Your account cannot read that resource (RBAC). Ask for read access
Empty report or few resources Wrong namespace: add -A, or set -n
No CPU or memory checks metrics-server is not installed or not working
Score seems too harsh Exclude accepted findings in the spinach file, or lower the noise with -l warn
Results differ from a colleague Different context, namespace, or Popeye version. Compare popeye version
Emoji look broken Use a UTF-8 terminal, or use -o jurassic for plain text
CI job always fails Popeye returns an error on problems. Use --force-exit-zero, or set a --min-score
Flag not recognized Options change between versions. Run popeye --help
Tips
Run Popeye regularly (weekly or nightly) and watch the score trend, not just one number.
Start with popeye -A -l warn. It hides the passing checks and shows what needs work.
Fix the cheap wins first: missing probes, "latest" tags, and missing resource requests and limits.
Use a spinach file to exclude things you accept, so the report stays useful.
Popeye is read-only. It is safe to run against production, but it still needs read access to many resources.
Pair it with kube-score in CI: kube-score checks manifests before deploy, Popeye checks what actually runs.
Install metrics-server so the utilization checks work.
Save HTML or JSON reports (--save) to compare before and after a cleanup.
In k9s you can run :popeye for the same report inside the dashboard.
Popeye's options and check codes change between versions. Run popeye --help and read the project README for your version.