rune port-forward
Open ad-hoc TCP tunnels from your laptop to a Rune-managed service or instance.
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.
Examples
Foreground
# 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
Detached
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.
# 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
Port specs
Each positional arg after the target is one of:
| Form | Meaning |
|---|---|
N | local N ↔ remote N |
LOCAL:REMOTE | local LOCAL ↔ remote REMOTE |
Local 0 and remote 0 are rejected. Multiple specs may be given.
Flags
| Flag | Default | Notes |
|---|---|---|
-n, --namespace | context | Target namespace. |
--bind <addr> | 127.0.0.1 | Local bind address. Use 0.0.0.0 to expose to the LAN. |
--instance <id> | first running | Pin to a specific instance for scale>1 services. |
-d, --detach | false | Hand the forward off to the local daemon (see below). |
Targeting
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/mongoorinstance/mongo-0skip 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.
Behavior
- 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/SIGTERMcleanly 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.
Detached mode
-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
ECONNREFUSEDburst. - 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 entersfailedstatus, visible inlist.
rune port-forward list columns:
| Column | Meaning |
|---|---|
ID | fwd-XXXXXXXX identifier (random per-forward). |
LOCAL | Bind address of the first listener. (+N) when there are more. |
TARGET | <namespace>/<service-or-instance>. |
STATUS | active, reconnecting, failed, unauthenticated. |
AGE | Time since the forward was created. |
Daemon limitations (v1)
- 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.)
Permissions
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.
See also
rune exec— full interactive shell (much wider blast radius)rune logs— stream container logs- Logs & exec — debug workflow guide
- Service
expose:— for persistent, multi-user exposure (different problem)