Using Kftray and Zellij
Updated:
If you spend any real time developing against a Kubernetes cluster — minikube on your laptop, a shared dev cluster, whatever — you end up with the same painful pattern: five terminal tabs, each holding a kubectl port-forward, a minikube tunnel, or a minikube service command that has to stay running for the day to make sense. Close the wrong tab and half your app stops working.
This post covers two small tools I keep coming back to that make that dance a lot less annoying. Kftray turns port-forwards into named, saved configs you can start and stop like services. Zellij is a Rust-based terminal multiplexer with layouts — a single command spins up every long-running process you need, in the right panes, at the right time. Together they replace the “wall of tabs” I used to keep open when working against my Cloudera Streaming Operators stack on minikube.
Kftray
kftray Graphical UI to start/stop multiple saved port forward configs.
- https://kftray.app/downloads
- https://kftray.app/docs/getting-started/quick-start
Create the config ~/.config/kftray/config.json:
[
{
"alias": "kafka",
"context": "my-cluster",
"namespace": "cld-streaming",
"workload_type": "service",
"service": "my-cluster-kafka-bootstrap",
"protocol": "tcp",
"local_port": 9092,
"remote_port": 9092,
"local_address": "0.0.0.0"
},
{
"alias": "web-efm",
"context": "my-cluster",
"namespace": "cld-streaming",
"workload_type": "service",
"service": "efm",
"protocol": "tcp",
"local_port": 10090,
"remote_port": 10090,
"local_address": "0.0.0.0"
}
]
Run it with this:
kftui
Zellij
Zellij is a modern, Rust-based terminal workspace and multiplexer.
- https://zellij.dev/about/
- https://zellij.dev/documentation/
Create the layout: ~/.config/zellij/layout/kube-service-ports-mac-cso-observability.kdl
layout {
split_direction "Horizontal"
pane {
command "/opt/homebrew/bin/minikube"
args "mount" "/Users/steven.matison/Documents/GitHub/NiFi2 Processor Playground/nifi-custom-processors/:/extensions" "--uid" "10001" "--gid" "10001"
}
pane {
command "bash"
args "-lc" "sudo /opt/homebrew/bin/minikube tunnel"
}
pane {
command "/opt/homebrew/bin/minikube"
args "service" "efm" "-n" "cld-streaming"
}
pane {
command "/opt/homebrew/bin/minikube"
args "service" "cloudera-surveyor-service" "--namespace" "cld-streaming"
}
pane {
command "/opt/homebrew/bin/minikube"
args "service" "prometheus-grafana" "--namespace" "cld-streaming"
}
pane {
command "/usr/local/bin/kubectl"
args "port-forward" "--address" "0.0.0.0" "service/my-cluster-kafka-bootstrap" "9092:9092" "-n" "cld-streaming"
}
pane {
command "/usr/local/bin/kubectl"
args "port-forward" "--address" "0.0.0.0" "service/efm" "10090:10090" "-n" "cld-streaming"
}
}
Pro Tip! Notice the main command format. Notice all additional args are each “individually” “quoted” “like” “this”
Run it with this:
zellij --layout kube-service-ports-mac-cso-observability
Update: split_direction is backwards from what you’d guess
I have a second layout on the Windows gaming PC (via WSL2) that fronts the cld-streaming cluster’s EFM and Kafka external access for the rest of my array — ~/.config/zellij/layouts/kube-service-ports-efm.kdl. It grew to 15 panes once I added Tailscale-bound forwards for a third array machine (a Beelink on Starlink): Kafka needs 4 separate forwards (bootstrap + one per broker), each doubled for LAN + Tailscale, plus EFM doubled the same way. All of that was split_direction "Horizontal"-stacked into one unreadable column.
The gotcha: Horizontal stacks panes top-to-bottom (splits along rows). Vertical puts panes side-by-side (splits along columns) — backwards from what the name suggests if your instinct is tmux’s -h/-v. Confirmed straight from zellij’s own source (pane_size.rs: SplitDirection::Vertical => self.cols.is_percent(), SplitDirection::Horizontal => self.rows.is_percent()) and the bundled strider.kdl example, which uses split_direction="Vertical" to put a narrow file-explorer sidebar next to a main pane.
To get a real left/right split — services on the left, all the Kafka forwards grouped on the right — nest a pane with its own split_direction inside a top-level pane. The container pane doesn’t run a command itself; its children do:
layout {
split_direction "Vertical"
pane split_direction="Horizontal" size="50%" {
pane {
command "bash"
args "-lc" "exec minikube tunnel"
}
pane {
command "/usr/local/bin/kubectl"
args "port-forward" "--address" "192.168.1.121" "svc/efm" "10090:10090" "-n" "cld-streaming"
}
pane {
command "/usr/local/bin/kubectl"
args "port-forward" "--address" "100.68.113.126" "svc/efm" "10090:10090" "-n" "cld-streaming"
}
# ... Surveyor, vLLM, cso-operator-app, MiNiFi agent panes follow the same pattern
}
pane split_direction="Horizontal" size="50%" {
pane {
command "/usr/local/bin/kubectl"
args "port-forward" "--address" "192.168.1.121" "svc/my-cluster-kafka-external-bootstrap" "31623:9094" "-n" "cld-streaming"
}
pane {
command "/usr/local/bin/kubectl"
args "port-forward" "--address" "100.68.113.126" "svc/my-cluster-kafka-external-bootstrap" "31623:9094" "-n" "cld-streaming"
}
# ... same LAN + Tailscale pair repeated for my-cluster-combined-0/1/2
}
}
Each Kafka/EFM service gets one pane bound to the LAN IP (192.168.1.121, for NvidiaNano and anything else on the local network) and a second pane bound to the Tailscale IP (100.68.113.126, for array machines outside the LAN — like the Beelink on Starlink). Same svc and port, different --address.
Using Kftray and Zellij
If you would like a deeper dive, hands on experience, demos, or are interested in speaking with me further about Using Kftray and Zellij please reach out to schedule a discussion.