I like kubectl. I like k9s.
But there are Kubernetes workflows where I kept wanting a fast local GUI:
- searching logs across pods
- keeping several port-forwards visible
- inspecting events next to resource status
- comparing YAML and Helm revisions
- switching between clusters without losing context
- debugging incidents without opening five terminal panes
Most Kubernetes desktop clients are built with web technology. That gives you cross-platform support, but Kubernetes is a demanding workload for a desktop UI.
A useful Kubernetes client is not just a CRUD dashboard. It has to handle real-time watchers, log streams, large tables, metrics refreshes, YAML editing, terminals, and port-forward sessions at the same time.
So I started building Krust, a native macOS Kubernetes client with a Rust core and Swift/AppKit UI.
Why Kubernetes is hard on desktop UIs
A Kubernetes UI is constantly processing change.
Pods restart. Deployments roll out. Events arrive. Metrics refresh. Logs stream. Users switch namespaces, inspect YAML, open terminals, and start port-forwards while all of that is happening.
The expensive part is not one API call.
The expensive part is everything happening together:
- rendering frequently changing resource tables
- keeping logs searchable without freezing the UI
- managing concurrent streams
- avoiding memory growth during long sessions
- keeping the app responsive during an incident
In a production cluster, even a normal resource view can mean hundreds or thousands of rows. A log viewer may need to hold 100k+ lines while still letting the user search instantly.
That is where architecture starts to matter.
The Rust + Swift split
Krust uses Rust for the Kubernetes/data layer and Swift/AppKit for the UI.
Rust handles:
- Kubernetes API communication
- async watchers
- log streaming
- parsing and filtering
- search
- shared state
- long-running background work
Swift/AppKit handles:
- native macOS windows and panels
- tables
- keyboard shortcuts
- inspectors
- editor and log UI
- system integration
The two sides communicate through FFI bindings.
This keeps the UI thread focused on rendering and interaction, while Rust does the heavier concurrent work in the background.
Example: logs need bounded memory
Kubernetes logs are unbounded by nature. A pod can emit thousands of lines per second.
A desktop log viewer cannot just append forever.
Krust uses a bounded in-memory buffer. When the buffer reaches its cap, old lines are evicted. Line numbers still remain stable, so search results can point to the original stream position even after eviction.
The hot path stays simple:
- receive line from Kubernetes stream
- parse minimal metadata
- store bounded raw line
- send display-ready row to the UI
- never block the main thread
Search is intentionally simple too. For the current buffer size, a linear scan in Rust is fast enough and avoids maintaining a complicated index for data that is constantly streaming and evicting.
Sometimes the boring solution is the right one.
Why not replace kubectl or k9s?
That is not the goal.
kubectl is still the source of truth for automation and scripting. k9s is excellent for terminal-first Kubernetes work.
Krust is for workflows where a local GUI helps:
- visual resource inspection
- multi-pod log debugging
- port-forward management
- Helm history and diffs
- multi-cluster context
- incident investigation from a Mac
The point is not GUI instead of CLI.
The point is using the right surface for the workflow.
Local-first matters
A Kubernetes desktop client has access to sensitive infrastructure context.
So Krust is designed to be local-first:
- uses your existing kubeconfig
- no required account
- no cloud sync
- no hosted control plane
- no telemetry requirement inside the app
For infrastructure tools, trust is part of the product.
The tradeoff
The obvious downside is that Krust is macOS-only.
Electron would make cross-platform distribution easier. Native macOS gives access to mature system UI components, lower overhead, better keyboard behavior, and a desktop experience that feels less like a browser shell.
That is the tradeoff I am making for now.
Try it
Krust is currently a free beta.
Website: https://krust.io
I would love feedback from Kubernetes users: what GUI workflow still feels painful for you today?
Top comments (1)
The detail about bounded memory for log streams — using a simple linear scan because the buffer is small enough that a fancy index isn't worth it — is the kind of quiet architectural honesty that tends to get lost in technical writing. There's a pressure to justify every decision with sophistication, when often the right call is just "this is fast enough for the actual data size, and anything more complex would be solving a problem we don't have."
What interests me about the Rust + Swift split specifically is that it inverts the usual pattern. Most "native" desktop apps that need heavy lifting either accept the overhead of a web runtime or push the work to a sidecar process. Here the heavy side is embedded directly in the app binary through FFI, which means the latency-sensitive data processing and the UI rendering are sharing the same process space without sharing the same thread concerns. That's a genuinely different architecture from both the Electron approach and the traditional background-daemon pattern.
I do wonder about the long-term maintenance cost of those FFI bindings, though. Swift and Rust both evolve quickly, and the binding layer is code that neither compiler can fully verify. It's the kind of thing that works beautifully until a macOS update shifts something subtle in AppKit's threading expectations, and then you're debugging across a language boundary. Probably worth it for the performance, but I'd be curious how much surface area that binding layer actually covers and whether it's turned out to be more or less fragile than expected.