lukaszraczylo 2e6db9ae2f Add kportal screenshot to documentation
- Add screenshot to README.md
- Add screenshot to GitHub Pages site with styling
- Include screenshot image in docs/ directory
2025-11-23 18:35:48 +00:00
2025-11-23 17:51:35 +00:00
2025-11-23 18:35:37 +00:00
2025-11-23 18:12:13 +00:00
2025-11-23 17:51:35 +00:00
2025-11-23 17:51:35 +00:00
2025-11-23 17:51:35 +00:00
2025-11-23 17:51:35 +00:00
2025-11-23 15:24:51 +00:00
2025-11-23 17:51:35 +00:00
2025-11-23 15:24:51 +00:00

kportal

Release License Go Report Card

Modern Kubernetes port-forward manager with interactive terminal UI

kportal simplifies managing multiple Kubernetes port-forwards with an elegant, interactive terminal interface. Built with Bubble Tea, it provides real-time status updates, automatic reconnection, and hot-reload configuration support.

kportal Screenshot

Features

  • 🎯 Interactive TUI - Beautiful terminal interface with keyboard navigation (↑↓/jk, Space to toggle, q to quit)
  • 🔄 Auto-Reconnect - Automatic retry with exponential backoff on connection failures (max 10s)
  • Hot-Reload - Update configuration without restarting - changes applied automatically
  • 🏥 Health Checks - Real-time port forward status monitoring with 5-second intervals
  • 🎨 Multi-Context - Support for multiple Kubernetes contexts and namespaces
  • 📦 Batch Management - Manage all port-forwards from a single configuration file
  • 🔌 Toggle Forwards - Enable/disable individual port-forwards on the fly with Space key
  • 🚀 Grace Period - Smart 10-second grace period to avoid false "Error" status on startup
  • 📊 Status Display - Clear visual indicators: Active (●), Starting (○), Reconnecting (◐), Error (✗)
  • 🔍 Error Reporting - Detailed error messages displayed below the table
  • 🔄 Pod Restart Handling - Detects and reconnects to pods when they restart
  • 🏷️ Label Selector Support - Dynamically target pods using label selectors
  • 📋 Prefix Matching - Automatically find and reconnect to pods with name prefixes
  • 🚫 Port Conflict Detection - Validates port availability before starting with detailed PID info
  • 🎭 Alias Support - Cleaner, more readable display names for your forwards

📦 Installation

Homebrew (macOS/Linux)

brew install lukaszraczylo/tap/kportal

Quick Install Script

curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash

Manual Download

Download the latest binary for your platform from the releases page:

  • macOS: kportal-{version}-darwin-{amd64|arm64}.tar.gz
  • Linux: kportal-{version}-linux-{amd64|arm64}.tar.gz
  • Windows: kportal-{version}-windows-{amd64|arm64}.zip

Build from Source

git clone https://github.com/lukaszraczylo/kportal.git
cd kportal
make build
make install

🚀 Quick Start

  1. Create a configuration file (.kportal.yaml):
contexts:
  - name: production
    namespaces:
      - name: backend
        forwards:
          - resource: service/postgres
            protocol: tcp
            port: 5432
            localPort: 5432
            alias: prod-db

      - name: frontend
        forwards:
          - resource: service/redis
            protocol: tcp
            port: 6379
            localPort: 6380
            alias: prod-redis
  1. Run kportal:
kportal
  1. Navigate the interface:
    • ↑↓ or j/k - Navigate through forwards
    • Space or Enter - Toggle forward on/off
    • q - Quit application

📖 Configuration

Simple Configuration

contexts:
  - name: <context-name>
    namespaces:
      - name: <namespace-name>
        forwards:
          - resource: <resource-type>/<resource-name>
            protocol: tcp
            port: <remote-port>
            localPort: <local-port>
            alias: <friendly-name>  # Optional

Advanced Configuration

contexts:
  # Production cluster
  - name: prod-us-west
    namespaces:
      - name: databases
        forwards:
          # Direct pod connection with prefix matching
          - resource: pod/postgres-primary
            protocol: tcp
            port: 5432
            localPort: 5432
            alias: prod-postgres

          # Service connection
          - resource: service/redis-master
            protocol: tcp
            port: 6379
            localPort: 6379
            alias: prod-redis

          # Pod with label selector
          - resource: pod
            selector: app=mongodb
            protocol: tcp
            port: 27017
            localPort: 27017
            alias: mongo

      - name: applications
        forwards:
          - resource: deployment/api-server
            protocol: tcp
            port: 8080
            localPort: 8080
            alias: api

  # Development cluster
  - name: dev-local
    namespaces:
      - name: default
        forwards:
          - resource: service/grafana
            protocol: tcp
            port: 3000
            localPort: 3000
            alias: grafana-dashboard

Configuration Options

Field Type Required Description
resource string Yes Kubernetes resource with type prefix (e.g., service/name, pod/name)
protocol string Yes Connection protocol (typically tcp)
port int Yes Remote port on the Kubernetes resource
localPort int Yes Local port to forward to
alias string No Friendly name for display (defaults to resource name)
selector string No Label selector for dynamic pod selection (e.g., app=nginx,env=prod)

Resource Formats

  • Pod by name: pod/pod-name or just pod-name
  • Pod by prefix: pod/my-app (matches my-app-xyz789, my-app-abc123, etc.)
  • Pod by selector: Set resource: pod and use selector: app=nginx
  • Service: service/service-name or svc/service-name
  • Deployment: deployment/deployment-name or deploy/deployment-name

🎮 Usage

Interactive Mode (Default)

kportal

Starts the interactive TUI where you can:

  • View all configured port-forwards in a table
  • See real-time status updates (Active, Starting, Reconnecting, Error)
  • Toggle forwards on/off with Space key
  • View detailed error messages at the bottom of the screen

Verbose Mode

kportal -v

Runs in verbose mode with:

  • Detailed logging to stdout
  • Periodic status table updates every 2 seconds
  • Full error traces
  • No interactive controls (for automation/debugging)

Validate Configuration

kportal --check

Validates your configuration file without starting any forwards:

  • Checks YAML syntax
  • Validates all required fields
  • Detects duplicate local ports
  • Shows validation errors with line numbers

Custom Configuration File

kportal -c /path/to/config.yaml

Version Information

kportal --version
# Output: kportal version 0.1.5

🔄 kftray Migration

Migrate from kftray JSON configuration:

kportal --convert configs.json --convert-output .kportal.yaml

Example conversion:

kftray JSON:

[
  {
    "service": "postgres",
    "namespace": "default",
    "local_port": 5432,
    "remote_port": 5432,
    "context": "production",
    "workload_type": "service",
    "protocol": "tcp",
    "alias": "prod-db"
  }
]

Converts to kportal YAML:

contexts:
  - name: production
    namespaces:
      - name: default
        forwards:
          - resource: service/postgres
            protocol: tcp
            port: 5432
            localPort: 5432
            alias: prod-db

🎨 Status Indicators

Indicator Status Description
● Active 🟢 Green Port-forward is active and healthy
○ Starting 🟡 Yellow Initial connection in progress (10s grace period)
◐ Reconnecting 🟡 Yellow Attempting to reconnect after failure
✗ Error 🔴 Red Connection failed - see error details below table
○ Disabled Gray Port-forward manually disabled via Space key

🛠️ Advanced Features

Hot-Reload

kportal automatically watches for configuration file changes and reloads:

# Edit your config while kportal is running
vim .kportal.yaml

# Changes are applied automatically within seconds:
# - New forwards are started
# - Removed forwards are stopped
# - Existing forwards continue running unchanged

Supports manual reload via SIGHUP:

kill -HUP $(pgrep kportal)

Health Checks

Built-in health monitoring system:

  • Check interval: Every 5 seconds
  • Timeout: 2 seconds per check
  • Grace period: 10 seconds for new connections
  • Automatic updates: Real-time status changes in UI
  • Error tracking: Detailed error messages for failed connections

Error Display

When connections fail, errors are displayed below the table:

Errors:
  • prod-postgres: dial tcp 127.0.0.1:5432: connect: connection refused
  • prod-redis: i/o timeout after 2.0s

Errors automatically clear when:

  • Connection becomes healthy
  • Forward is disabled
  • Forward is removed

Port Conflict Detection

kportal checks for port conflicts at multiple stages:

At startup:

Port conflicts detected:
  Port 8080:
    • Requested by: api-server (context: prod, namespace: default)
    • Currently used by: PID 1234 (chrome)

During hot-reload:

  • Only validates new ports being added
  • Skips currently managed ports
  • Rejects configuration if conflicts found

Pod Restart Handling

When a pod restarts:

  1. Port-forward connection breaks
  2. kportal immediately re-resolves the resource:
    • For prefix matches: Finds newest pod with matching prefix
    • For selectors: Re-queries pods with matching labels
  3. Reconnects to new pod
  4. Logs the switch: Switched to new pod: old-pod-abc → new-pod-xyz

Retry Strategy

Exponential backoff with maximum interval:

  • Intervals: 1s → 2s → 4s → 8s → 10s (max)
  • Infinite retries: Continues until connection succeeds
  • Independent: Each forward has its own retry logic
  • Grace period: First 10 seconds show "Starting" instead of "Error"

🔧 Development

Prerequisites

  • Go 1.23 or higher
  • Access to a Kubernetes cluster
  • kubectl configured with contexts

Building

# Build binary
make build

# Run tests
make test

# Run all checks (fmt, vet, staticcheck, test)
make all

# Check current version
make version

# Install locally
make install

# Install system-wide
sudo make install-system

# Clean build artifacts
make clean

Project Structure

kportal/
├── cmd/kportal/              # Main application entry point
├── internal/
│   ├── config/               # Configuration parsing and validation
│   ├── forward/              # Port-forward manager and workers
│   │   ├── manager.go        # Orchestrates all forwards
│   │   ├── worker.go         # Individual forward worker
│   │   └── port_checker.go   # Port conflict detection
│   ├── healthcheck/          # Health monitoring system
│   │   └── checker.go        # Port health checking
│   ├── k8s/                  # Kubernetes client wrapper
│   │   ├── client.go         # K8s client management
│   │   ├── port_forward.go   # Port-forward implementation
│   │   └── resolver.go       # Resource resolution
│   ├── retry/                # Retry logic with backoff
│   │   └── backoff.go        # Exponential backoff
│   ├── ui/                   # Terminal UI implementations
│   │   ├── bubbletea_ui.go   # Interactive TUI (Bubble Tea)
│   │   └── table_ui.go       # Simple table for verbose mode
│   └── converter/            # kftray JSON converter
├── Formula/                  # Homebrew formula
├── .github/workflows/        # CI/CD pipelines
│   └── release.yml           # Release automation
├── install.sh                # Installation script
├── semver.yaml               # Semantic version config
├── Makefile                  # Build automation
└── README.md                 # This file

📝 Examples

Database Access

contexts:
  production:
    namespaces:
      databases:
        - resource: postgres-primary
          port: 5432
          local_port: 5432
          alias: prod-db

Connect with:

kportal  # Start in another terminal
psql -h localhost -p 5432 -U postgres

Multiple Services

contexts:
  dev:
    namespaces:
      default:
        - resource: api
          port: 8080
          local_port: 8080
        - resource: frontend
          port: 3000
          local_port: 3000
        - resource: redis
          port: 6379
          local_port: 6379

Access:

  • API: http://localhost:8080
  • Frontend: http://localhost:3000
  • Redis: redis-cli -p 6379

Cross-Context Setup

contexts:
  prod-us:
    namespaces:
      backend:
        - resource: api
          port: 8080
          local_port: 8080
          alias: prod-us-api

  prod-eu:
    namespaces:
      backend:
        - resource: api
          port: 8080
          local_port: 8081  # Different local port
          alias: prod-eu-api

Compare APIs across regions simultaneously.

Debug Multiple Pod Versions

contexts:
  production:
    namespaces:
      default:
        # Version 1
        - resource: pod
          selector: app=myapp,version=v1
          port: 8080
          local_port: 8080
          alias: app-v1

        # Version 2
        - resource: pod
          selector: app=myapp,version=v2
          port: 8080
          local_port: 8081
          alias: app-v2

        # Debug port for v2
        - resource: pod
          selector: app=myapp,version=v2
          port: 6060
          local_port: 6060
          alias: app-v2-pprof

🐛 Troubleshooting

Port Already in Use

Problem: Port 8080: already in use by PID 1234 (chrome)

Solutions:

# Find the process
lsof -i :8080

# Kill the process
kill 1234

# Or use a different local port in config
local_port: 8081

Connection Refused

Problem: dial tcp 127.0.0.1:8080: connect: connection refused

Common causes:

  1. Pod not ready yet - Wait for status to change from "Starting" → "Active" (10s grace period)
  2. Wrong port number - Verify the pod/service actually exposes that port
  3. Service not exposed - Check with kubectl get svc and kubectl describe svc <name>

Debug:

# Check pod status
kubectl get pods -n <namespace>

# Check if port is exposed
kubectl describe pod <pod-name> -n <namespace>

# Check service endpoints
kubectl get endpoints <service-name> -n <namespace>

Context Not Found

Problem: context "prod" not found in kubeconfig

Solution:

# List available contexts
kubectl config get-contexts

# Verify context name matches
kubectl config current-context

# Update your config to use the correct context name

Health Check Errors During Startup

Problem: Seeing "Error" status immediately after starting

This is normal! kportal has a 10-second grace period. If the connection is still failing after 10 seconds, check:

  • Pod is running: kubectl get pods
  • Port is correct in config
  • Network connectivity to cluster

Logs Covering UI

Problem: Kubernetes client logs appearing over the interactive UI

This is fixed in v0.1.5+. The interactive mode now completely suppresses all logs including:

  • Standard Go log package
  • Kubernetes klog output
  • Any stderr/stdout leakage

If you still see logs, please file an issue!

🤝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes and add tests
  4. Run checks: make all
  5. Commit your changes (follow semantic commit messages)
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

Semantic Versioning

This project uses semver-gen for automatic semantic version generation based on git commit messages.

Version Keywords:

  • Patch (0.0.X): fix, bugfix, hotfix, patch, docs, test, refactor
  • Minor (0.X.0): feat, feature, add, enhance, update, improve
  • Major (X.0.0): breaking, major, BREAKING CHANGE

Example commits:

git commit -m "feat: add health check grace period"  # Bumps minor version
git commit -m "fix: resolve port conflict detection" # Bumps patch version
git commit -m "breaking: change config file format"  # Bumps major version

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Built with Bubble Tea by Charm - An awesome framework for building terminal UIs
  • Styled with Lipgloss - Terminal styling library
  • Inspired by kftray - Original GUI port-forward manager
  • Uses client-go for Kubernetes integration
  • Version management by semver-gen

📚 Documentation

Signal Handling

  • Ctrl+C / SIGTERM: Graceful shutdown (closes all forwards)
  • SIGHUP: Reload configuration file

Made with ❤️ by Lukasz Raczylo

S
Description
Languages
Go 98.3%
Shell 0.8%
Makefile 0.7%
Ruby 0.2%