Run Kubernetes port-forwards from one YAML file
+kportal starts every forward listed in .kportal.yaml, shows their status in a terminal UI, reconnects dropped forwards and reloads the file when it changes. It also runs without the UI.
Installation
-Get started in seconds
-Homebrew
-macOS
-brew install --cask lukaszraczylo/taps/kportal
- Quick Install
-All platforms
-curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash
- Verifies SHA-256 against the release checksums file. If cosign is on PATH, the keyless cosign signature is verified too. Set DRY_RUN=1 to preview, SKIP_COSIGN=1 to bypass cosign.
Manual Download
-Linux, macOS, Windows (amd64 & arm64)
-brew install --cask lukaszraczylo/taps/kportalUsage
-Simple commands, powerful results
-Interactive Mode
-kportal
- Launch the interactive TUI with real-time status updates and keyboard controls.
-Verbose Mode
-kportal -v
- Run with detailed logging for debugging and automation.
-Validate Config
-kportal --check
- Validate configuration without starting any forwards.
-Custom Config
-kportal -c /path/to/config.yaml
- Use a custom configuration file instead of .kportal.yaml
-Select Contexts
-kportal --context development-team-a
- Forward only the contexts you name instead of all of them. Repeatable and comma-separated. Contexts never selected together can reuse the same local ports.
-Headless Mode
-kportal -headless -v 2>kportal.log &
- Run without TUI for scripting; logs are emitted on stderr.
-Bulk Generate
-kportal generate --context=my-cluster
- Discover services in a cluster and bulk-add forwards with consecutive local ports. Add --dry-run to preview.
Keyboard Shortcuts
-
-
- Status Indicators
-Features
+| Forwards | service/<name>, pod/<name> (exact name or prefix), or pod with a label selector. TCP only. |
|---|---|
| Terminal UI | Add, edit, delete and toggle forwards without a restart. The add wizard writes the config file. |
| Table columns | tui.columns sets which columns the forwards table shows, their order and width. It applies to the UI and the -verbose table. |
| Add wizard | If the cluster does not allow listing namespaces, the wizard asks you to type the namespace. |
| Reconnect | Exponential backoff: 1s, 2s, 4s, 8s, then 10s, with 10% jitter. It does not stop retrying. |
| Pod restarts | Forwards by pod prefix or selector connect to the new pod. |
| Health checks | tcp-dial or data-transfer, every 3s by default. Stale connections are reconnected by age and idle time. |
| Hot reload | File watcher and SIGHUP. An invalid config is rejected and the previous one stays active. |
| Contexts | Several contexts and namespaces in one file. --context selects which contexts run. |
| Port conflicts | Startup and reload checks report a busy local port and the process that holds it. |
| mDNS | With mdns.enabled: true, each forward is reachable as <alias>.local. |
| HTTP log | Per-forward request log with details, filter, search, JSON formatting, gzip decoding and body copy. Sensitive header values are redacted. |
| Benchmark | HTTP requests through a forward. Reports min, max and average latency, P50, P95, P99, requests per second and status codes. |
| Generate | kportal generate lists cluster services and appends the ones you pick, with consecutive local ports. |
| Headless | -headless runs without the UI and logs to stderr. -log-format json switches the log format. |
| Import | --convert turns a kftray JSON file into kportal YAML. |
| Shell completion | kportal completion prints or installs bash, zsh and fish completions. |
Configuration
-Simple YAML configuration with powerful options
-Install
+Homebrew (macOS)
+brew install --cask lukaszraczylo/taps/kportalInstall script
+curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bashThe script verifies the archive SHA-256 against the release checksums file. If cosign is on PATH, it also verifies the keyless signature. Set DRY_RUN=1 to download and verify without installing. Set SKIP_COSIGN=1 to skip the cosign check.
Manual download
+The releases page has archives for Linux, macOS and Windows on amd64 and arm64.
+Build from source
+git clone https://github.com/lukaszraczylo/kportal.git
+cd kportal
+make build && make install
+ The build needs Go 1.26 or later.
+contexts:
+
+ Usage
+ Quick start
+ Create .kportal.yaml, then run kportal.
+ contexts:
- name: production
namespaces:
- name: backend
@@ -794,34 +200,122 @@
localPort: 5432
alias: prod-db
- - resource: pod/nginx
+ - resource: service/api
protocol: tcp
- port: 80
+ port: 8080
localPort: 8080
- httpLog: true # log HTTP traffic
- mdns:
- enabled: true
+ Commands and flags
+
+
+ Command or flag Effect
+
+ kportalStart the terminal UI.
+ -c <file>Config file. Default .kportal.yaml.
+ -vVerbose logging.
+ -headlessNo UI. Logs go to stderr.
+ -checkValidate the config and exit.
+ -context <name>Forward only the named contexts. Repeatable, or comma-separated.
+ -log-format text|jsonLog format. Default text.
+ -convert <file>Convert a kftray JSON file. -convert-output sets the output, default .kportal.yaml.
+ -updateCheck for updates and exit.
+ -versionPrint the version and exit.
+ kportal generateAdd forwards from a cluster. Needs --context. --config picks the file and --dry-run only prints.
+ kportal completionPrint a completion script. --shell bash|zsh|fish, --install, --uninstall.
+
+
+
-healthCheck:
+ Examples
+ kportal -check
+kportal -c /path/to/config.yaml
+kportal --context development-team-a
+kportal -headless -v 2>kportal.log &
+kportal generate --context=my-cluster --dry-run
+kportal completion --shell zsh --install
+ With --context, contexts you never select together can reuse a localPort. Without it, a duplicate local port is an error.
+
+
+
+ Keys
+
+
+ Forwards list
+
+
+
+ Up Down / j k Move
+ PgUp PgDn Move by 10 rows
+ Space / Enter Toggle forward
+ n Add forward
+ e Edit forward
+ d Delete forward
+ b Benchmark
+ l HTTP log
+ q Quit
+
+
+
+ In the add and edit wizard, h on the confirmation step toggles httpLog.
+
+
+ Status
+
+
+
+ ● Active Connection healthy
+ ○ Starting First connection. Failed checks show Starting for 10s.
+ ◐ Reconnecting Reconnecting after a failure
+ ✗ Error Connection failed
+ ○ Disabled Turned off in the UI
+
+
+
+
+
+
+
+
+ Configuration
+ Forward fields
+
+
+ Field Required Description
+
+ resourceYes service/<name>, pod/<name>, or pod with selector
+ protocolNo tcp. No other value is accepted.
+ portYes Remote port
+ localPortYes Local port
+ aliasNo Display name and mDNS hostname
+ selectorWith bare pod Label selector, for example app=nginx. Not allowed with pod/<name>.
+ httpLogNo true, or a map of options
+
+
+
+
+
+
+ Health checks and reliability
+ healthCheck:
interval: "3s"
+ timeout: "2s"
method: "data-transfer"
maxConnectionAge: "25m"
+ maxIdleTime: "10m"
+
+reliability:
+ tcpKeepalive: "30s"
+ dialTimeout: "30s"
+ retryOnStale: true
+ watchdogPeriod: "30s"
+ These are the defaults. method is tcp-dial or data-transfer. A connection past maxConnectionAge reconnects only when it is also idle.
+
+
+ mDNS
+ mdns:
+ enabled: true
contexts:
- name: production
@@ -831,281 +325,93 @@ contexts:
- resource: service/postgres
port: 5432
localPort: 5432
- alias: db # db.local
-
-
- Without an alias, service/redis publishes redis.local.
Resource Types
--
-
service/nameService
- pod/namePod by name
- pod/prefixPod by prefix
- deployment/nameDeployment
-
Label Selectors
--
-
resource: pod
- selector: app=nginx
- - Dynamically resolves to running pods -
Forward Options
--
-
resource- Target resource
- port- Remote port
- localPort- Local port
- alias- Display name & mDNS
- selector- Label selector
-
Table columns
+tui:
+ columns:
+ - name: alias
+ width: 30 # optional, 1-200
+ - name: resource
+ - name: local
+ - name: status
+ Valid names: context, namespace, alias, type, resource, remote, local, status. List order is render order and unlisted columns are hidden. Without tui, the table keeps its default columns. The setting applies to the UI and the -verbose table.
Advanced Features
-Production-ready capabilities
-Reload
+kportal reloads when the file changes. To reload by hand, run kill -HUP $(pgrep kportal).
Health Checks
-Advanced health monitoring prevents stale connections during long operations.
-HTTP log and benchmark
+Press l on a forward that has httpLog set. The list shows TIME, METHOD, STATUS, LATENCY and PATH.
httpLog:
+ enabled: true
+ includeHeaders: true # sensitive values are redacted
+ maxBodySize: 65536 # bytes, 0 = unlimited
+ filterPath: "/api/" # log only paths containing this text
+ logFile: "api.log" # also append entries to this file
+ List view
+-
+
- Enter opens details +
- g / G jump to top or bottom +
- a toggles auto-scroll +
- f cycles the filter: All, Non-2xx, Errors +
- / searches path or method +
- c clears filters, q closes +
Detail view
+-
+
- Headers, bodies and timing +
- JSON is pretty-printed, gzip and deflate are decoded +
- c copies the response body +
- Esc, q or Enter returns to the list +
With includeHeaders: true, values of Authorization, Cookie, Set-Cookie, X-Api-Key, X-Auth-Token, X-Csrf-Token, Proxy-Authorization, X-Access-Token and any header whose name contains token, secret, password or apikey become [REDACTED]. This cannot be turned off.
Benchmark
+Press b on a forward. Set the URL path (default /), method, concurrency and request count.
mDNS Hostnames
-Access forwards via .local hostnames without editing /etc/hosts.
-alias: prod-db
- →
- prod-db.local
- service/redis
- →
- redis.local
- Works on macOS & Linux
-More
+The README covers troubleshooting, kftray import and telemetry opt-out. Release history is in the changelog.
+Hot Reload
-Configuration changes are applied automatically without restarting.
--
-
- • File watcher detects changes in ~100ms -
- • New forwards start automatically -
- • Removed forwards stop gracefully -
- • Existing forwards continue -
Retry Strategy
-Exponential backoff with infinite retries ensures reliability.
-HTTP Traffic Logging
-Press l to view real-time HTTP traffic.
-httpLog
- Benchmarking
-Press b to benchmark connections.
-