Skip to content

rune port-forward

Terminal window
rune port-forward [flags] <service-or-instance> [LOCAL:]REMOTE [more ports...]

rune port-forward opens a local TCP listener and forwards every accepted connection over the API server’s gRPC stream into a port on a running service instance. It’s the Rune-native replacement for ssh root@host -L ... + docker inspect-the-bridge-IP — same shape as kubectl port-forward, with an optional detached mode for daily-use forwards.

Terminal window
# localhost:27017 → mongo:27017 in the default namespace
rune port-forward mongo 27017
# Map a different local port (avoids collision with a local mongod)
rune port-forward mongo 27018:27017 -n shared
# Multiple ports at once
rune port-forward flo 9002 9001 -n shared
# Expose the forward to the LAN (default is 127.0.0.1)
rune port-forward --bind 0.0.0.0 mongo 27017
# Pin to a specific instance for a scaled service
rune port-forward --instance mongo-1 mongo 27017

For daily-use forwards (Compass against the dev Mongo, browser at the flo dashboard) the -d flag hands the forward off to a small per-user daemon so it survives terminal closes and laptop sleep.

Terminal window
# Background a forward — prints the assigned ID and pid
rune port-forward -d mongo 27017
# ✓ Forward fwd-a3f29ab1 started: 127.0.0.1:27017 -> shared/mongo (pid 88421)
# What's running?
rune port-forward list
# Recent activity for a forward
rune port-forward logs fwd-a3f29ab1
# Tear one down
rune port-forward stop fwd-a3f29ab1
# Tear them all down
rune port-forward stop --all

Each positional arg after the target is one of:

FormMeaning
Nlocal N ↔ remote N
LOCAL:REMOTElocal LOCAL ↔ remote REMOTE

Local 0 and remote 0 are rejected. Multiple specs may be given.

FlagDefaultNotes
-n, --namespacecontextTarget namespace.
--bind <addr>127.0.0.1Local bind address. Use 0.0.0.0 to expose to the LAN.
--instance <id>first runningPin to a specific instance for scale>1 services.
-d, --detachfalseHand the forward off to the local daemon (see below).

The positional <service-or-instance> is resolved the same way as rune exec and rune logs:

  • A bare service name (e.g. mongo) selects the first Running instance of that service in the namespace.
  • An instance ID (e.g. mongo-0) targets that exact instance.
  • Explicit prefixes service/mongo or instance/mongo-0 skip the auto-detect and force a kind.

For scaled services (scale > 1), --instance pins the forward to a specific instance ID so reconnects always land on the same container.

  • The CLI binds every local listener up-front. Bind errors (port already in use, EACCES on a privileged port) surface before any “Forwarding…” output.
  • All accepted local connections share the same server-side gRPC stream — multiplexed by an internal conn_id. This keeps the wire layer cheap even when a browser opens many parallel connections to the forwarded port.
  • SIGINT / SIGTERM cleanly drains in-flight bytes and exits 0.
  • If the bound instance restarts, the foreground command exits with a clear error and a non-zero status; re-run to reconnect. The detached daemon does this automatically with exponential backoff.

-d spawns (or reuses) a small daemon — one per user — that owns the local listener and the gRPC stream. State lives at ~/.rune/forwards/:

~/.rune/forwards/
├── daemon.pid
├── daemon.sock # unix socket (mode 0700)
├── daemon.log # daemon stdout/stderr
└── fwd-<id>.json # one file per active forward

The daemon:

  • Multiplexes any number of forwards over a single process.
  • Reconnects to runed automatically with exponential backoff (1s → 60s, up to 10 attempts) when the stream errors — local listeners stay bound during reconnect so clients don’t see a ECONNREFUSED burst.
  • Exits on its own 60 seconds after all forwards are stopped.
  • Re-creates persisted forwards on startup (after a panic / kill -9); a forward whose dial fails enters failed status, visible in list.

rune port-forward list columns:

ColumnMeaning
IDfwd-XXXXXXXX identifier (random per-forward).
LOCALBind address of the first listener. (+N) when there are more.
TARGET<namespace>/<service-or-instance>.
STATUSactive, reconnecting, failed, unauthenticated.
AGETime since the forward was created.
  • Unix only. macOS and Linux are supported. Windows users get foreground only.
  • Per-user. One daemon per user-home; not shared across machines or users on the same box.
  • No auto-start on boot. After a reboot, re-run rune port-forward -d. (launchd / systemd-user integration is a future ticket.)

Gated by the port-forward verb on the services resource — narrower than services.exec since port-forward can only reach already-listening ports, never a shell.

It is deliberately not part of the built-in readwrite policy. Grant it explicitly:

apiVersion: rune.io/v1
kind: Policy
metadata:
name: dev-debug
rules:
- resource: services
verbs: [get, list, logs, port-forward]
namespace: shared

Then attach the policy to the operator’s user via rune admin policy attach.