Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Kubernetes port-forward manager with interactive terminal UI
kportal manages multiple Kubernetes port-forwards with an interactive terminal interface. It provides real-time status updates, automatic reconnection, hot-reload configuration, and mDNS hostname publishing.
✨ Features
- Interactive TUI - Terminal interface with keyboard navigation
- Live management - Add, edit, and delete port-forwards without restarting
- Auto-reconnect - Exponential backoff retry on connection failures
- Hot-reload - Configuration changes applied automatically
- Health monitoring - Multiple check methods with stale connection detection
- Multi-context - Support for multiple Kubernetes contexts and namespaces
- Pod restart handling - Automatic reconnection when pods restart
- Label selectors - Dynamic pod targeting using label selectors
- Port conflict detection - Validates port availability with PID information
- mDNS hostnames - Access forwards via
.localhostnames - HTTP traffic logging - Real-time HTTP request/response logging for debugging
- Connection benchmarking - Built-in HTTP benchmarking with latency statistics
- Headless mode - Background operation for scripting and automation
🔄 Comparison with Other Tools
| Feature | kportal | k9s | Kube Forwarder | kftray |
|---|---|---|---|---|
| Interface | Terminal TUI | Terminal TUI | Desktop GUI (Electron) | Desktop GUI + TUI |
| Persistent Config | ✅ YAML file | ❌ Session only | ✅ JSON bookmarks | ✅ JSON + Git sync |
| Auto-reconnect | ✅ Exponential backoff | ❌ Manual | ✅ Basic | ✅ Watch API |
| Hot-reload Config | ✅ File watch + SIGHUP | ❌ | ❌ | ❌ |
| Health Checks | ✅ TCP + data-transfer | ❌ | ❌ | ❌ |
| Stale Connection Detection | ✅ Age + idle tracking | ❌ | ❌ | ❌ |
| HTTP Traffic Logging | ✅ Built-in viewer | ❌ | ❌ | ✅ |
| Connection Benchmarking | ✅ Built-in | ✅ Via Hey | ❌ | ❌ |
| mDNS Hostnames | ✅ .local domains |
❌ | ❌ | ❌ |
| Label Selectors | ✅ | ✅ | ❌ | ✅ |
| Multi-context | ✅ | ✅ | ✅ | ✅ |
| Headless Mode | ✅ | ❌ | ❌ | ❌ |
| System Tray | ❌ | ❌ | ❌ | ✅ |
| UDP Support | ❌ | ❌ | ❌ | ✅ Proxy relay |
| Dependencies | Single binary | Single binary | Electron | Tauri + kubectl |
📦 Installation
Homebrew (macOS/Linux)
brew install lukaszraczylo/taps/kportal
Quick Install
curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash
Manual Download
Download binaries from the releases page.
Build from Source
git clone https://github.com/lukaszraczylo/kportal.git
cd kportal
make build && make install
🚀 Quick Start
Create .kportal.yaml:
contexts:
- name: production
namespaces:
- name: backend
forwards:
- resource: service/postgres
protocol: tcp
port: 5432
localPort: 5432
alias: prod-db
- resource: service/api
protocol: tcp
port: 8080
localPort: 8080
alias: api
httpLog: true # Enable HTTP traffic logging
Run:
kportal
Keyboard Controls
| Key | Action |
|---|---|
↑↓ / j/k |
Navigate |
Space / Enter |
Toggle forward |
n |
Add new forward |
e |
Edit forward |
d |
Delete forward |
b |
Benchmark connection |
l |
View HTTP logs |
q |
Quit |
📖 Configuration
Basic Structure
contexts:
- name: <context-name>
namespaces:
- name: <namespace-name>
forwards:
- resource: <type>/<name>
protocol: tcp
port: <remote-port>
localPort: <local-port>
alias: <display-name> # optional
selector: <label-selector> # optional
httpLog: true # optional - enable HTTP logging
Forward Options
| Field | Required | Description |
|---|---|---|
resource |
Yes | Resource type and name (e.g., service/postgres, pod/my-app) |
protocol |
Yes | Protocol (tcp) |
port |
Yes | Remote port |
localPort |
Yes | Local port |
alias |
No | Display name and mDNS hostname |
selector |
No | Label selector for pod resolution |
httpLog |
No | Enable HTTP traffic logging (true/false) |
Resource Formats
| Format | Description |
|---|---|
service/name |
Service forwarding |
pod/name |
Direct pod by name |
pod/prefix |
Pod by prefix (matches prefix-*) |
pod + selector |
Pod by label selector |
deployment/name |
Deployment |
Health Check Configuration
healthCheck:
interval: "3s" # Check frequency
timeout: "2s" # Check timeout
method: "data-transfer" # tcp-dial or data-transfer
maxConnectionAge: "25m" # Reconnect before k8s timeout
maxIdleTime: "10m" # Detect idle connections
reliability:
tcpKeepalive: "30s"
dialTimeout: "30s"
retryOnStale: true
Health check methods:
tcp-dial- Fast TCP connection testdata-transfer- Verifies tunnel functionality by attempting data read
Connection age reconnection only triggers when the connection is also idle, preventing interruption of active transfers like database dumps.
mDNS Hostnames
Enable mDNS to access forwards via .local hostnames:
mdns:
enabled: true
contexts:
- name: production
namespaces:
- name: default
forwards:
- resource: service/postgres
port: 5432
localPort: 5432
alias: prod-db # Accessible via prod-db.local:5432
- Explicit
aliasbecomes<alias>.local - Without alias, hostname is generated from resource name (
service/redis→redis.local) - Works on macOS (Bonjour) and Linux (avahi-daemon)
Verify registration:
dns-sd -B _kportal._tcp local # macOS
avahi-browse -t _kportal._tcp # Linux
Usage
Interactive Mode
kportal
Verbose Mode
kportal -v
Headless Mode
Run without TUI for scripting and automation:
kportal -headless
Combines well with verbose mode for background operation:
kportal -headless -v &
Validate Configuration
kportal --check
Custom Config File
kportal -c /path/to/config.yaml
Status Indicators
| Indicator | Description |
|---|---|
● Active |
Connection healthy |
○ Starting |
Initial connection (10s grace period) |
◐ Reconnecting |
Reconnecting after failure |
✗ Error |
Connection failed |
○ Disabled |
Manually disabled |
Advanced Features
HTTP Traffic Logging
Press l in the TUI to view real-time HTTP traffic for a selected forward. The log viewer shows:
| Column | Description |
|---|---|
| TIME | Request timestamp |
| METHOD | HTTP method (GET, POST, etc.) |
| STATUS | Response status code |
| LATENCY | Request duration |
| PATH | Request path |
List view shortcuts:
| Key | Action |
|---|---|
↑/↓ |
Navigate entries |
Enter |
View request details |
g/G |
Jump to top/bottom |
a |
Toggle auto-scroll |
f |
Cycle filter mode (All → Non-2xx → Errors) |
/ |
Search by path or method |
c |
Clear all filters |
q |
Close log viewer |
Detail view:
Press Enter on any entry to see full request/response details including:
- Request and response headers (alphabetically sorted)
- Request and response bodies
- Timing information and status codes
| Key | Action |
|---|---|
↑/↓ |
Scroll content |
PgUp/PgDn |
Scroll by page |
g |
Jump to top |
c |
Copy response body to clipboard |
Esc/q |
Return to list |
Body display features:
- JSON formatting - JSON bodies are pretty-printed with syntax highlighting
- Compression handling - gzip/deflate content is automatically decompressed
- Binary detection - Binary content shows a placeholder instead of garbled data
Filter modes:
- All - Show all entries
- Non-2xx - Hide successful (2xx) responses
- Errors - Show only 4xx and 5xx responses
Connection Benchmarking
Press b in the TUI to benchmark a selected forward. Configure:
- URL Path - Target endpoint (default:
/) - Method - HTTP method (GET, POST, etc.)
- Concurrency - Number of parallel workers
- Requests - Total number of requests
Results include:
- Success/failure counts
- Min/Max/Avg latency
- P50/P95/P99 percentiles
- Throughput (requests/sec)
- Status code distribution
Hot-Reload
Configuration changes are applied automatically. Manual reload:
kill -HUP $(pgrep kportal)
Port Conflict Detection
kportal validates port availability at startup and during hot-reload, showing which process is using conflicting ports.
Retry Strategy
Exponential backoff: 1s → 2s → 4s → 8s → 10s (max). Retries continue indefinitely until connection succeeds.
Migration from kftray
kportal --convert configs.json --convert-output .kportal.yaml
Signal Handling
Ctrl+C/SIGTERM- Graceful shutdownSIGHUP- Reload configuration
🐛 Troubleshooting
Port Already in Use
lsof -i :<port>
kill <pid>
Connection Refused
- Verify pod is running:
kubectl get pods -n <namespace> - Verify port is correct:
kubectl describe pod <pod> - Check service endpoints:
kubectl get endpoints <service>
Context Not Found
kubectl config get-contexts
🔧 Development
Prerequisites
- Go 1.23+
- Kubernetes cluster access
- kubectl configured
Build
make build # Build binary
make test # Run tests
make all # fmt, vet, staticcheck, test
make install # Install locally
Contributing
See CONTRIBUTING.md for guidelines.
License
MIT License - see LICENSE.
Acknowledgments
- Bubble Tea - Terminal UI framework
- Lipgloss - Terminal styling
- client-go - Kubernetes client
- kftray - Inspiration
