Per-terminal-tab kubeconfig isolation for interactive Kubernetes work.
kubetab provides isolated kubeconfig files for each terminal tab, automatically synced from a single base kubeconfig (~/.kube/config). Each tab can switch contexts and namespaces independently without affecting other tabs.
Key Features:
- 🔒 Per-tab isolation - Each terminal tab gets its own private kubeconfig
- 🔄 Auto-sync - Changes to base kubeconfig automatically propagate to all tabs
- 💾 Preserve local state - Tab-specific namespace and context selections persist across syncs
- 🎯 Zero shell complexity - Single line in
.zshrc, no background jobs or shell scripts - 🚀 Fast and reliable - Built in Go with fsnotify + polling fallback
- 🍎 macOS native - Designed for iTerm2 + zsh (works on other terminals too)
- Go 1.21 or later
- macOS (primary target), Linux compatible
git clone https://github.com/eladnoor/kubetab.git
cd kubetab
make installThis installs kubetab to ~/.local/bin/kubetab. Ensure ~/.local/bin is in your PATH.
Add this single line to your ~/.zshrc:
[[ -o interactive && -n "$ITERM_SESSION_ID" ]] && eval "$(kubetab init --shell zsh --preserve ns)"Optional but recommended - add an alias for kubecm:
alias kc='kubecm --config "$KUBECONFIG"'Restart your shell or run source ~/.zshrc.
- Initialization: When you open a terminal tab,
kubetab initcreates a private kubeconfig file for that tab - Watching: A background watcher process monitors your base kubeconfig for changes
- Syncing: When base config changes, the watcher updates your tab's config while preserving tab-local settings
- Isolation: Each tab's
KUBECONFIGenvironment variable points to its own file
~/.kube/
├── config # Base kubeconfig (source of truth)
└── sessions/
├── kubeconfig.w0t0p0.yaml # Tab-specific kubeconfig
├── kubetab.w0t0p0.pid # Watcher PID
├── kubetab.w0t0p0.log # Watcher log
└── kubetab.w0t0p0.base.sig # Base config signature cache
Initialize kubetab for the current terminal tab:
kubetab init [flags]Common flags:
--shell {zsh|bash|fish|posix}- Shell output format (default:zsh)--base <path>- Base kubeconfig path (default:~/.kube/config)--preserve {ns|all}- What to preserve across syncs (default:ns)ns- Preserve namespace changes onlyall- Preserve namespace, cluster, and user overrides
--watch {auto|fsnotify|poll|off}- Watch mode (default:auto)--poll-interval <duration>- Polling interval (default:2s)
Show status of current tab:
kubetab statusExample output:
Kubetab Status
==============
Session ID: w0t0p0_645B9C4C-5F71-4DD5-B4C1-5E6AC4317ED1
Base config: /Users/you/.kube/config
Tab config: /Users/you/.kube/sessions/kubeconfig.w0t0p0_645B9C4C-5F71-4DD5-B4C1-5E6AC4317ED1.yaml
Log file: /Users/you/.kube/sessions/kubetab.w0t0p0_645B9C4C-5F71-4DD5-B4C1-5E6AC4317ED1.log
Tab config: exists (size: 2847 bytes, modified: 2025-12-14T10:30:45-08:00)
Watcher: running (PID: 12345)
Log file: exists (size: 1523 bytes, modified: 2025-12-14T10:30:45-08:00)
Remove stale session files from closed tabs:
kubetab cleanup [--dry-run]Run the watcher daemon (usually started automatically by init):
kubetab watch --base <path> --out <path> --id <id> [flags]You typically don't run this manually.
Preserves only namespace changes for each context:
- You switch to context
prodin Tab 1, set namespace toapp-ns - Base config is updated with a new context
staging - Tab 1 keeps context
prodwith namespaceapp-ns - Tab 2 can independently work with
stagingcontext
Preserves namespace, cluster, and user overrides:
- Useful if you temporarily modify cluster endpoints or auth info per-tab
- More complex, usually not needed for typical usage
- Uses fsnotify to watch base kubeconfig directory
- Runs concurrent polling every 2s as fallback
- Best reliability across different scenarios (symlinks, atomic writes, etc.)
- Pure fsnotify watching
- Faster event detection but may miss some edge cases
- Pure polling
- Most compatible but slightly higher latency
- No watching, only initial sync
- You must manually re-run
kubetab initto refresh
# ~/.zshrc
[[ -o interactive && -n "$ITERM_SESSION_ID" ]] && eval "$(kubetab init --shell zsh)"
alias kc='kubecm --config "$KUBECONFIG"'eval "$(kubetab init --base ~/my-kube/config --shell zsh)"eval "$(kubetab init --preserve all --shell zsh)"eval "$(kubetab init --watch off --shell zsh)"Tab 1 - Production work:
$ kc switch prod
$ kc ns production-app
$ kubectl get pods
# Working in prod context, production-app namespaceTab 2 - Staging work (independent):
$ kc switch staging
$ kc ns staging-app
$ kubectl get pods
# Working in staging context, staging-app namespaceTab 3 - Development:
$ kc switch dev
$ kubectl get pods
# Working in dev context, default namespaceMeanwhile, if your base kubeconfig gets updated (e.g., cloud auth refresh, new context added), all tabs automatically see the update without losing their tab-local context/namespace settings.
kubetab statustail -f ~/.kube/sessions/kubetab.*.logkubetab cleanup# Find and kill old watcher
kubetab cleanup
# Reinitialize (starts new watcher)
eval "$(kubetab init)"Ensure ITERM_SESSION_ID is set:
echo $ITERM_SESSION_IDIf empty, you're not in iTerm2 or it's not exporting the session ID.
- Primary:
ITERM_SESSION_IDenvironment variable - Fallback:
TERM_SESSION_IDor random UUID - Sanitized to be filesystem-safe
- All kubeconfig writes are atomic (write to temp file, then rename)
- Prevents corruption from concurrent access
- SHA-256 hash of base kubeconfig content
- Efficient no-op when base hasn't changed
- Works correctly with symlinks and atomic replacements
- All files created with
0600permissions (user read/write only) - Sessions directory created with
0700permissions - No sensitive data in logs (only metadata)
- Primary: macOS + iTerm2 + zsh
- Also works: macOS Terminal, Linux terminals, bash, fish
- Not tested: Windows (WSL might work)
Contributions welcome! Please open issues or PRs on GitHub.
MIT License - see LICENSE file for details
Inspired by the need for better kubectl context management in multi-environment workflows.