A bird's-eye view of your clusters. Faster than anything.
English | 한국어
PigeonEye is a fast, native Kubernetes GUI that you drive like a CLI. Every
resource type your cluster serves — including every CRD — shows up
automatically, with the same columns kubectl get prints.
A quick look
Every resource your cluster serves, in one sidebar. Built-in kinds are
grouped the way you think about them, and every CRD follows on its own — 138
of them here, with nothing to configure. Pin the kinds you live in with p
and gather them into groups of your own. Each cluster gets a tab, like a
browser, and ⌘\ splits the window so two clusters sit side by side.
A list that keeps up. Search matches any field of every object, not just
the name. Columns are yours to show, hide, reorder or add — CPU and memory
against requests and limits included. share view copies a peye:// link
that opens the same cluster, kind, namespace and row for whoever clicks it.
Everything about one object, without leaving the list. Logs, shell, a
debug container and port-forward are one click away — and l follows the
logs of a whole Deployment, one pod, or a single container. Related objects
link straight through, and the manifest edits in place: dry-run shows
what the server would change before you Apply.
A GUI you drive like a CLI
The screen of a GUI, the hands of a CLI. Point and click when you want to —
but everything you do day to day has a key: moving around, searching,
opening logs and shells, editing, scaling, deleting, switching clusters,
splitting the view. The keys are the ones your fingers already know from
vim, k9s and kubectl.
| Key | Does |
|---|---|
: |
Go anywhere by typing — : deploy kube-system, : pod app=api, : pod @prod |
/ |
Search every field of every row |
j k · ↵ |
Move · open the detail |
l · s · e |
Logs · shell · edit the manifest |
d · r · space |
Delete · rollout restart · mark rows for a bulk action |
tab · ⌘\ |
Next cluster · split into two views |
? |
Every shortcut, on one sheet |
Why it's fast
Measured on a production cluster with 23,770 pods and 171,267 events:
| PigeonEye | Full-object list (what informer clients sync on connect) | |
|---|---|---|
| First pod screen | ~0.35s / 350 KB | 60s+ and 136 MB, still incomplete |
| Discovery on connect | 0.18s, one request | one round-trip per API group |
The table renders only the rows on screen (virtual scroll), so filtering a 24k-row list stays at ~0.5ms per keystroke. Open lists then stay live over a watch, and revisiting a view resumes the watch from cache — no re-list — so only what changed since arrives.
Staying live is cheap too. A pod changing on that 24k-row list lands in ~0.2ms: the event is matched against the rows it names rather than re-indexing the whole list, and however many pods a rollout retires at once, they leave in a single pass.
Where the speed comes from: server-side printer columns (the Table API, one request per view — no full-object sync), streaming pagination, virtual scrolling, a lazily-built full-text index, watch-based incremental updates coalesced into batches, and a view cache that resumes the watch on return.
Highlights
- Every resource, zero config — kinds come from the Discovery API, so
every CRD shows up automatically with the columns
kubectl getprints. - Columns your way — hide, drag-reorder, and add custom columns from
any field or label (
kubectl -o custom-columns, but click instead of type). Pin frequently-used kinds into your own groups — and organize the clusters themselves into named context groups too. - Search that finds things — full-text over every field, plus regex,
!negation, per-column value filters, and numeric comparisons (> 500). - Search every open cluster at once —
: pod api-serverasks all of them and says which cluster each answer came from; picking one switches tab, kind and namespace and lands the cursor on the row. Costs nothing until you ask, and the clusters you have browsed answer from memory. - Split into two live views —
⌘\puts two fully independent panes side by side: different clusters, kinds and namespaces, each with its own watch, search, sort and detail panel.⌘←/⌘→moves focus; every shortcut acts on the focused pane. - Cross-cluster Issues — one ⚠ list of pods in a bad state and nodes
that aren't Ready, gathered from every connected cluster and refreshed
in the background.
↵jumps straight to the resource in its own tab. The list is windowed, so it opens as fast with four thousand problems as with four — which matters, because it is longest exactly when something is on fire. - Rollout at a glance — Deployments, StatefulSets, DaemonSets, ReplicaSets and Argo Rollouts carry a dot while replicas are still spreading, so a half-finished rollout is visible without opening it. Open one and it says where the rollout actually is — Rolling out, Applying (the spec changed, the controller hasn't acted on it yet), Not fully available, Paused, Stuck or Synced — with the replica and generation numbers behind the verdict, refreshing itself until it settles.
- Why a pod isn't up, in the list — the status column says Pending;
the Recent event column next to it says
FailedScheduling ×3 · 45s, and hovering it gives you0/50 nodes are available: insufficient cpu. Only warnings appear there, so an empty cell means nothing is wrong. One older than five minutes is dimmed rather than dropped: the pod may be fine now, but that warning is often why it restarted. - Share the exact view — copy a
peye://link to the view, or to a single resource. Whoever opens it lands on the same cluster, kind, namespace and row. - A real log viewer — follow, previous (crashed) container, since windows, timestamps, in-view search, copy/download, and combined workload logs.
- Shells & debugging — pod exec, a transparent privileged node shell, and ephemeral debug containers that mirror the pod's security context (pass restricted PodSecurity / Kyverno).
- Safe edits & operating — YAML server-side apply with dry-run,
create-from-template, scale, rollout restart / history / undo, drain,
CronJob trigger, secret reveal, port-forward manager, live
topmetrics, andauth can-i. - Logins that can ask you things — an expired token re-logs in from a
terminal inside the app (AWS SSO, gcloud, Teleport, Azure, OIDC), so a
CLI that wants an OTP or a passphrase has somewhere to ask. A kubeconfig
execblock holds one command and has no "run this first" hook, so each context can also carry a pre-connect command of its own —tsh login, a bastion tunnel, a VPN — run before connecting and kept on your machine rather than in a shared kubeconfig. A cluster whose credentials have genuinely expired is parked out of background polling until you sign in again, so a credential helper that opens a browser (aws-vault,oidc-login) is never re-run on a timer behind your back. - Color-coded multi-cluster tabs, light/dark, and in-app updates — the
top bar flags a new release and upgrades in place, no
brewneeded.
vs. k9s & Lens
| PigeonEye | k9s | Lens/OpenLens | |
|---|---|---|---|
| Native GUI | ✅ | terminal | Electron |
| Every daily action from the keyboard | ✅ | ✅ | mouse-first |
| Cold-start on a 24k-pod cluster | ~0.35s first screen | fast | slow (full sync) |
| All CRDs / aggregated APIs, no config | ✅ | ✅ | ✅ |
| Regex / negation / numeric / per-column filters | ✅ | regex/! |
basic |
| Logs: previous · since · search · download | ✅ | ✅ | ✅ (Lens only) |
| Combined workload/Service logs | ✅ | ✅ | partial |
| Ephemeral debug containers (policy-safe) | ✅ | — | — |
| Node shell (nsenter) | ✅ | opt-in | ✅ |
| Secret decode / reveal | ✅ | ✅ | ✅ |
| Server dry-run before apply | ✅ | — | — |
| Create from templates | ✅ | blank YAML | ✅ |
| Rollout history / undo | ✅ | ✅ | ✅ |
| CronJob trigger / suspend | ✅ | ✅ | — |
| Node & pod metrics columns (metrics-server) | ✅ | ✅ | needs Prometheus |
auth can-i / permissions |
✅ | reverse-lookup | RBAC views |
| Search by name across every open cluster | ✅ | — | — |
| Two independent panes side by side | ✅ | — | — |
| Problem resources across every cluster in one list | ✅ | — | — |
| Deep link to an exact view / resource | ✅ | — | — |
| Auth re-login, including CLIs that prompt (OTP) | ✅ | — | — |
Per-context pre-connect command (tunnel / tsh login) |
✅ | — | — |
| Time-series metric charts | — | — | ✅ (Prometheus) |
| Helm / extensions / xray tree / linters | — | xray/popeye/plugins | Helm/extensions |
Metric graphs, Helm, an owner xray tree, and cluster linters are the main things PigeonEye doesn't (yet) do; navigation between owners/children is one click via related-object jumps.
Platform support
| Platform | Status |
|---|---|
| macOS (Apple Silicon / Intel) | ✅ Supported |
| Linux (x86_64) | ✅ Supported |
| Windows (x86_64) | ✅ Supported |
Install
macOS — Homebrew
brew tap tackish/pigeoneye
brew trust tackish/pigeoneye # third-party taps need a one-time trust
brew install --cask peyeThen launch it from anywhere — Spotlight, or just type peye in a terminal:
Windows — download the -setup.exe installer from
Releases (an .msi is
there too, for deploying by policy). The installer isn't code-signed yet,
so SmartScreen will ask: More info → Run anyway.
Linux — download the .deb / .rpm / .AppImage from
Releases.
Build from source (Rust stable + Node 20+):
npm install npm run tauri build # installers in src-tauri/target/release/bundle/ npm run tauri dev # or run directly
Getting started
-
Launch. PigeonEye reads your default kubeconfig chain (
$KUBECONFIG, falling back to~/.kube/config). No other setup. -
Connect a cluster with the “+ add context” dropdown in the top bar. Each cluster opens as a tab — open as many as you want; requests to different clusters run in parallel. Tabs and the last active cluster are restored on next launch.
-
More kubeconfig files? Open ⚙ settings and add file paths (e.g.
~/.kube/staging-config). All files are merged into the context list, and each context remembers which file it came from. -
Cluster needs something first? Some clusters are only reachable after a login or a tunnel that the kubeconfig cannot express. Hit ⋯ next to a context in either picker and give it a command — it runs in a terminal before the connection, so anything it asks you, you can answer:
tsh status >/dev/null 2>&1 || tsh login --proxy=teleport.example.com:443 -
Cluster behind a bastion or a corporate proxy? PigeonEye honours a cluster's
proxy-url:— SOCKS5 or HTTPCONNECT— andHTTPS_PROXY, so a cluster you can only reach through a jump host connects like any other. It reads the same linekubectldoes, so the two never drift, and it is per-cluster: everything else keeps going out directly.- cluster: server: https://XXXX.gr7.ap-northeast-2.eks.amazonaws.com proxy-url: socks5://127.0.0.1:1080 # e.g. an `ssh -D 1080` bastion
The proxy resolves the name, so a private endpoint that doesn't resolve on your laptop still works.
License
PolyForm Noncommercial 1.0.0 — free for personal and any noncommercial use. Commercial use is not permitted.


