k10ls is a friendly command-line tool that keeps network tunnels open to your Kubernetes workloads. It talks directly to the Kubernetes API (no kubectl subprocess) so it works reliably inside scripts and automation. Point k10ls at your clusters, list the Services or Pods you care about, and it continuously maintains the port-forwards for you.
✅ Forwards ports for Pods & Services
✅ Resolves services to pods automatically
✅ Runs natively with the Kubernetes API (no kubectl subprocess)
✅ Supports multiple contexts & configurations
✅ Written in Go for lightweight execution
- A Kubernetes cluster you can access (for example a local kind cluster or a managed cluster).
- Access to a kubeconfig file (usually
~/.kube/config). - Go 1.20 or later if you want to build from source.
Pick the asset that matches your operating system and CPU, download it, and place it somewhere on your PATH.
curl -LO https://github.com/besrabasant/k10ls/releases/latest/download/k10ls-linux-amd64
chmod +x k10ls-linux-amd64
sudo mv k10ls-linux-amd64 /usr/local/bin/k10lscurl -LO https://github.com/besrabasant/k10ls/releases/latest/download/k10ls-darwin-universal
chmod +x k10ls-darwin-universal
sudo mv k10ls-darwin-universal /usr/local/bin/k10lsInvoke-WebRequest -Uri "https://github.com/besrabasant/k10ls/releases/latest/download/k10ls-windows-amd64.exe" -OutFile "k10ls.exe"
# Optionally move k10ls.exe to a folder that is on your PATH.git clone https://github.com/besrabasant/k10ls.git
cd k10ls
make build
# The compiled binary is placed in ./bin/k10lsTo run the app directly from source without creating a binary:
make runk10ls reads a single TOML file that describes the port-forwards you want. Save your file as config.toml (or any name you prefer) and pass the path with --config.
Each configuration file has three layers:
- Global settings – apply to every Kubernetes context unless overridden.
- Context blocks – one block for each Kubernetes context (similar to
kubectl --context). - Resource lists – inside a context you can list Services, Pods, or label selectors that should be forwarded.
The table below explains every option in plain English. Each row also links to a short code sample.
| Field | Scope | What it does | Example |
|---|---|---|---|
global_kubeconfig |
Global | Path to a kubeconfig file used when a context does not specify its own. | Example |
default_address |
Global | IP address to bind the local ports to (use 0.0.0.0 to listen on all interfaces). |
Example |
[[context]] |
Per context | Starts a block that defines the Kubernetes context name and the resources you want to forward within it. | Example |
name |
Context | The name of the kubeconfig context to use (matches kubectl config get-contexts). |
Example |
namespace |
Context | Default namespace for resources in this context (falls back to default). |
Example |
address |
Context | Overrides default_address for all resources in this context. |
Example |
kubeconfig |
Context | Path to a kubeconfig file that should be used only for this context. | Example |
[[context.svc]] |
Service | Describes a Service whose backing pods should be forwarded. | Example |
[[context.pods]] |
Pod | Describes a single Pod that should be forwarded directly. | Example |
[[context.label-selectors]] |
Label selector | Forwards the first Pod that matches the given Kubernetes label selector. | Example |
name |
Service/Pod | Name of the Service or Pod to forward. | Example |
label |
Label selector | The label query used to find matching pods (e.g. app=api). |
Example |
ports |
Service/Pod/Selector | List of port mappings. Each item pairs a pod port in source with a local machine port in target. |
Example |
namespace |
Resource | Optional override of the namespace for this Service/Pod/Selector. | Example |
address |
Resource | Optional override of the bind address for this Service/Pod/Selector. | Example |
Port mapping terminology: For historical reasons the TOML uses
sourcefor the pod (remote) port andtargetfor the local port on your machine. k10ls automatically flips the values when it establishes the tunnel, so you still access pods using the familiarlocal:remotelayout. For example,ports = [{ source = "80", target = "8080" }]exposes pod port80on your laptop at8080.
Below are concise snippets that demonstrate how each field is used.
global_kubeconfig = "/home/alex/.kube/config"This tells k10ls to load that kubeconfig file whenever a context does not specify a dedicated kubeconfig.
default_address = "0.0.0.0"All port-forwards will listen on every network interface unless a context or resource overrides the address.
[[context]]
name = "kind-local"Creates a new section for the kind-local kubeconfig context.
[[context]]
name = "kind-local"
namespace = "demo"Resources inside this context will use the demo namespace unless they provide their own namespace field.
[[context]]
name = "kind-local"
address = "127.0.0.1"All forwards in this context bind to 127.0.0.1. This is useful when you only want to expose the ports on your laptop.
[[context]]
name = "prod-cluster"
kubeconfig = "/opt/configs/prod-kubeconfig"Only this context uses the custom kubeconfig file. Other contexts fall back to global_kubeconfig or the in-cluster configuration.
[[context]]
name = "kind-local"
[[context.svc]]
name = "mqtt"
ports = [
{ source = "1883", target = "1883" }
]k10ls finds the pods behind the mqtt Service and forwards local port 1883 to port 1883 on that pod.
[[context]]
name = "kind-local"
[[context.pods]]
name = "api-0"
ports = [
{ source = "8080", target = "8080" }
]Forwards directly to the Pod named api-0.
[[context]]
name = "kind-local"
[[context.label-selectors]]
label = "app=worker"
ports = [
{ source = "9090", target = "9090" }
]The first Pod with the label app=worker will be forwarded. k10ls will restart the forward if the pod changes.
ports = [
{ source = "80", target = "8080" },
{ source = "443", target = "9443" }
]Each mapping describes pod_port -> local_port. Because of historical naming, source refers to the port inside the pod and target is the port exposed on your machine. In the example above, the pod's port 80 is available locally on 8080, and pod port 443 is on 9443.
[[context.svc]]
name = "dashboard"
namespace = "monitoring"Overrides the context namespace when the Service lives elsewhere.
[[context.svc]]
name = "dashboard"
address = "0.0.0.0"Binds this Service forward to all interfaces even if the context default is more restrictive.
# save as config.toml
global_kubeconfig = "${HOME}/.kube/config"
default_address = "127.0.0.1"
[[context]]
name = "kind-local"
[[context.svc]]
name = "web"
ports = [
# source -> pod port, target -> port exposed on your machine
{ source = "80", target = "8080" }
]Run it with:
k10ls --config config.tomlYou can now open http://127.0.0.1:8080 in your browser, which reaches port 80 on the pod; in this mapping target denotes the port on your host machine while source points at the pod.
# config.multi.toml
global_kubeconfig = "${HOME}/.kube/config"
default_address = "0.0.0.0"
[[context]]
name = "kind-local"
namespace = "demo"
[[context.svc]]
name = "mqtt"
ports = [
# Each entry is pod source port -> host target port
{ source = "1883", target = "1883" },
{ source = "8883", target = "8883" }
]
[[context]]
name = "prod"
address = "127.0.0.1"
kubeconfig = "/opt/configs/prod-kubeconfig"
[[context.pods]]
name = "api-0"
namespace = "backend"
address = "0.0.0.0"
ports = [ { source = "8080", target = "9000" } ]
[[context.label-selectors]]
label = "app=worker"
ports = [ { source = "9090", target = "9090" } ]This configuration keeps tunnels open to two clusters. Notice how the prod pod overrides both the namespace and address, and exposes the pod's port 8080 on your machine at 9000.
k10ls --config /path/to/config.tomlIf you run without --config, k10ls looks for config.toml in the current directory.
When k10ls starts, it prints information about each context and forward. Here is a sample output:
INFO[0000] Processing context: kind-local
INFO[0001] Started port-forward for pod web-6c6cc7cc98-zx9p7 on [8080:80]
INFO[0001] Equivalent kubectl command: kubectl --context kind-local -n default port-forward pod/web-6c6cc7cc98-zx9p7 8080:80 --address 127.0.0.1
Processing contexttells you which kubeconfig context is being processed.Started port-forwardconfirms the tunnels are live and shows the actual pod name.Equivalent kubectl commandis a helpful hint if you want to reproduce the same port-forward manually.
k10ls keeps running until you stop it (Ctrl+C). If the pod restarts, k10ls will reconnect automatically.
If you are developing k10ls, these Makefile helpers can speed things up:
make build– compile the binary into./bin/k10ls.make run– rungo run ./...with your current configuration.make fmt– format the Go source code.make lint– rungolangci-lint.make tidy– clean upgo.modandgo.sum.
- Port already in use – choose a different
targetport (the port on your machine) or free it with tools likelsof -i :8080. - Authentication errors – double-check the
kubeconfigpaths and context names. Runningkubectl --context <name> get podsis a quick sanity check. - No pods found – ensure the Service has selectors or the label selector matches existing pods.
k10ls is released under the MIT License. Contributions are welcome!