Profile
Back to NewsBack
GitHub Trending 9 min
Reader Mode
Dudude-bit/rubick: Modern cross-platform Kubernetes GUI client built with Tauri and Rust

Dudude-bit/rubick: Modern cross-platform Kubernetes GUI client built with Tauri and Rust

16 hours ago

Rubick

A desktop Kubernetes client that tries not to lie to you.

Like Lens, k9s or Headlamp, it reads your kubeconfig and shows your clusters.
Unlike them, it treats "I don't know" as an answer worth giving.

Download</a>

rubick.tech — the website, with honest comparisons to Lens, k9s and Headlamp.

CI</a> Release</a> Downloads</a> License</a>

Free and GPLv3. No account, no telemetry.

!Rubick on a workload page

Why another one

It shows the failure, not the field. A pod whose container is crash-looping reports Running, because that is what .status.phase holds. A Service with healthy pods and a mistyped port name publishes nothing and still draws green. Rubick derives status the way kubectl does and reads the endpoints the cluster actually publishes, so those two stop being invisible.

It says where the path stops. Ingress → Service → pods is drawn on the workload's own page, and when nothing is behind an address it names the reason: a backend that does not exist, a selector matching nothing, or pods running but not ready.

It warns before it obeys. Scale, Restart, Delete and Edit YAML tell you who will undo the change and how fast — an autoscaler in seconds, Argo CD or Flux in minutes. Then it does what you asked, because a hand edit during an incident is legitimate.

It says what it cannot see. A desktop app does not inherit your shell's environment, so at startup Rubick asks your login shell for it, .zshrc and .bashrc included, and adopts what comes back: PATH, AWS_PROFILE, KUBECONFIG and the rest. Settings → Diagnostics says whether that worked, names the directories it actually searches, which plugins resolve in them, and what each context needs, and copies the lot, redacted, for when the machine with the problem is not yours.

!A path that stops, and why

What you get

| Area | What Rubick shows | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Workloads | Pods, Deployments, StatefulSets, DaemonSets, ReplicaSets, Jobs, CronJobs — with init containers as an ordered sequence, sidecars told apart from them, and CPU/memory over time | | Logs | Virtualised, multi-container, server-side filtering, repeat collapsing — and they open where the answer is: on a pod stuck in init, that means the failing container's previous run | | Shell | A real tab per pod whose session survives you looking elsewhere | | Network | Services, Ingresses, Endpoints and EndpointSlices, with the traffic chain above | | Gateway API | Gateways, all five route kinds, ListenerSets, classes and policies. A route says whether it is serving, and where it is not, which of the eight links between a listener and a pod broke — with the controller's own words. A route that attaches to a ListenerSet belongs to that set's Gateway and is judged there. A map draws the whole road; a probe from your machine answers what the cluster cannot | | Storage & config | PVs, PVCs, StorageClasses, ConfigMaps, Secrets — binary values shown as binary, private keys never revealed | | Custom resources | Every CRD, with YAML editing and validation | | Helm | Releases, revisions, rollback, uninstall |

!Logs open on the container that failed

Integrations

Detected ones need nothing from you — their CRDs are in the cluster or they are not. Configured ones need an address, and Rubick never goes looking for one.

| Integration | What it gives you | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Routing | Traefik, ingress-nginx, Istio — hosts, rules and middleware read as routing. nginx annotations become sentences with the raw key beside each; a configuration-snippet is shown verbatim and never paraphrased | | Certificates | cert-manager — expiry wherever TLS is named, and the issuance chain when renewal fails | | Delivery | Argo CD and Flux — every object says whether it is delivered, from which revision, and whether your edit will survive | | Metrics | Prometheus — real history, disk fullness and network traffic, none of which metrics.k8s.io can answer. VictoriaMetrics speaks the same API and is asked for the subpath it serves it under, /prometheus or /select//prometheus | | Logs | Loki — so a crashed pod's logs outlive the pod | | Clouds | GKE, EKS, AKS node pools, machine types, zones and spot status, read from labels with no cloud account | | Cloud ingress | GKE Ingress, the AWS Load Balancer Controller and AGIC — the certificate none of them keeps in spec.tls, and one row per ALB rather than per Ingress, because group.name puts several namespaces on one | | Local & nodes | k3s, k3d and minikube — a local cluster reads as local rather than as the cloud its name imitates. Karpenter — nodes reported under the pool that made them, and no pool called spot on the strength of one node |

Adding one costs a folder and a line — see CONTRIBUTING.

**Prometheus and Loki live in your cluster, and Rubick asks from your machine.** So http://prometheus.monitoring:9090 — the address every chart's README gives you — resolves to nothing here: it is a name only the cluster answers. Press Find it in this cluster in the connect dialog and Rubick forwards a local port to the Service and fills the address in for you. It picks the component that can answer a query, not the write path, and moves the forward to a new pod when the old one is rolled. An Ingress hostname or a LoadBalancer address works too, if you have one.

**AWS and Azure are the least exercised paths here, and issues are very
welcome.** Rubick is developed against a GKE cluster running Traefik and
cert-manager, so those are the ones that get looked at every day. The EKS
and AKS halves — ALB groups, IngressClassParams, ACM certificates, AGIC
annotations, Workload ID — were built from the controllers' documented
behaviour and covered with unit tests, not against a live cluster of either
kind. If something reads wrong on yours, that is worth an issue even without
a diagnosis: paste the objects and say what you expected. Being wrong about
your cluster is the one thing this app is not allowed to be.

!Integrations, with what each is doing

Getting around

Every name you can go to is a link, with the gestures you expect: click to peek, middle-click for a background tab, shift for a foreground one. Tabs carry a route and a scope, so several clusters stay open side by side. Search reaches across clusters — !cluster-name aims it. Light and dark, and identity colouring that still works in greyscale or with colour blindness.

English and Russian, switched in Settings. What gets translated is the app's own words; what the cluster wrote does not. A kind is called Pods in both, a status stays CrashLoopBackOff because its colour is looked up by that string, and a controller's message is quoted rather than paraphrased — a translated error is the app putting words in your cluster's mouth.

More screenshots

Connections, grouped by question
Connections — grouped by the question you are asking
Scaling something an autoscaler owns
Before you act — who will undo this, and how fast
Usage with real history
Usage — the watched window, or real ranges with Prometheus
Search, from anywhere
Search — across clusters, aimed with !cluster-name

!The same page in the light theme

Light theme — the same page, the same tokens.

What it deliberately does not do

  • No cost estimates. Committed use, sustained use, spot pricing and negotiated rates make them wrong more often than right, and a wrong number about money poisons the right ones.
  • No whole-cluster topology graph. Routing is a chain in fixed order, not a general graph; a force-directed blob looks like insight and answers nothing.
  • No editing routes or renewing certificates. Reading them well is a feature; writing them is a different one with a different blast radius — an ACME rate limit is five failures an hour.
  • No guessing. If a name in a log line might be an object, it stays text. If an integration was never asked, the app says _not looked at_ rather than leaving a gap that reads as _nothing there_.

Install

brew install --cask Dudude-bit/tap/rubick   # macOS

Or grab the build for your platform from Releases. On Arch, rubick-kubernetes-bin is in the AUR, maintained by @Prototik from the same .deb published here — thanks to him for it. There is no winget package: the Windows installers are unsigned, and winget installs are meant to run without a dialog in the way.

  • macOS — signed with a Developer ID certificate and notarised by Apple, so it opens on a double-click.
  • Windows — not signed, so SmartScreen will warn on the first launch. More info → Run anyway.
  • Linux.deb, .rpm and an .AppImage that needs nothing installed.
What it talks to. Your clusters, and GitHub for update checks. Nothing else: there is no analytics, no account and no crash reporting, and any integration you connect is an address you typed yourself.

Requirements. Kubernetes 1.21+ (EndpointSlices). Cloud login (EKS, GKE, AKS, OIDC, exec plugins) uses the same credentials kubectl does; Settings → Clusters shows each context, how it authenticates, and what is missing if it cannot.

Development

mise install   # Bun, Node, Rust
bun install    # the Tauri CLI comes with it
make dev       # run it
make build     # package it

Needs Bun 1.3+, Rust 1.91+, and the Tauri prerequisites for your platform.

bunx tsc --noEmit                                     # types
bun run lint                                          # eslint, zero warnings
bun run test                                          # frontend tests
cargo test --workspace                                # Rust tests

A block of lint rules keeps the codebase from drifting back, and each fails the commit: colours must be role tokens and no component may branch on the theme; nothing outside src/integrations/ may name a vendor; polling rates go through useLiveQuery rather than a hand-written interval; a StatusBadge keeps the untranslated status, because its colour is a table lookup; and