kubectl proxy --port 8001 --context my-cluster &
python3 container-relay.py --k8s-url http://127.0.0.1:8001
Container dashboard
A read-only dashboard for your own Docker and Kubernetes through a small local relay: containers, images, volumes and networks with live stats and logs; namespaces, workloads with rollout status, pods, services and endpoints, nodes, events, YAML, and a topology diagram — with every request copyable as curl, docker or kubectl.
Added
How to use it
A web page cannot open Docker’s unix socket, and the Kubernetes API needs credentials a page
should never hold. So this dashboard reads through container-relay.py, one Python file that runs
on your computer, listens on 127.0.0.1:8098, and forwards read-only requests to Docker and to
one Kubernetes cluster you choose. Without a relay the page shows a recorded demo — a few
containers and a one-node k3s cluster — so you can see what it does first.
With the gitwebsite app. If the gitwebsite app (or gw serve) is running on your computer,
Connect reads Docker and Kubernetes through its companion instead — the same read-only rules (GET
only, an allow-list of read endpoints, no exec/attach, Secrets refused), Docker’s socket found by
itself, and every context of your kubeconfig to choose from (the kubeconfig is never written and its
current context never changed). In the app’s own windows it connects by itself; from this site press
⚡ Use the gitwebsite app and approve the code in the app. The relay below is then not needed.
How to use it
-
Not needed if the gitwebsite app is running — otherwise download container-relay.py (also the Download the relay button) and run it:
python3 container-relay.py. It finds Docker’s socket by itself —/var/run/docker.sock, Rancher Desktop’s~/.rd/docker.sock, Docker Desktop’s~/.docker/run/docker.sock, Colima and OrbStack — or take--docker-host unix:///path(ortcp://host:2375). -
For Kubernetes, pick one of two ways. Either let kubectl do the authentication (this works with every auth plugin, OIDC and cloud login) and point the relay at it:
or let the relay read a kubeconfig itself:
python3 container-relay.py --kubeconfig ~/.kube/config --context my-cluster. It reads the file once, never writes it and never changes its current context; without--contextit uses the file’s current context. Tokens, token files, client certificates, basic auth and exec credential plugins work; for the oldauth-providerentries usekubectl proxy. -
Press Connect. The status line says what the relay reaches: the Docker API version, and the cluster with its context. Tick Connect when this page opens to skip this next time, and Refresh every to keep the tables current.
Docker
-
Containers — like
docker ps -a: name, state with health, status and uptime, image, ports (127.0.0.1:8080→80/tcp), age and ID. Click one for its summary (command, restart count and policy, exit code and OOM kill, networks and addresses, mounts, labels, recent health-check results), Logs (the last 50–1,000 lines or all, follow, timestamps; stderr in red), Live stats (CPU and memory charts from the stats stream) and Inspect (the full JSON). -
Stats — like
docker stats: CPU %, memory used / limit and %, network and block I/O, PIDs and a CPU sparkline for every running container, every few seconds. -
Images, Volumes, Networks — sizes, tags, how many containers use an image, subnets and the containers attached to a network; click for details.
Kubernetes — choose a namespace or all of them.
-
Workloads — Deployments, StatefulSets, DaemonSets, Jobs and CronJobs with ready / up-to-date / available counts and the rollout status in
kubectl rollout statuswords ("1 old replicas are pending termination", "exceeded its progress deadline"). Click one for its pods, conditions, events and YAML. -
Pods — the
kubectl get podscolumns: ready containers, the STATUS kubectl would print (CrashLoopBackOff,Init:0/1,Terminating,Completed), restarts with when the last one was, age, IP and node. Click one for its containers (state, last termination and exit code), conditions, QoS class, Logs (per container, follow, Previous for the instance that crashed), Events and YAML. -
Services — type, cluster IP, external address, ports and ready / total endpoints, read from EndpointSlices; click one to see each endpoint, its pod and node.
-
Nodes — ready state, roles, kubelet version, allocatable CPU and memory, pods running out of those allowed; click for capacity versus allocatable (what the system reserves), conditions such as
MemoryPressure, taints and the OS, kernel and runtime.
Events lists the namespace’s (or cluster’s) events newest first, with repeat counts ("x12 over
5m"), filtered to warnings or by text. Topology draws one namespace: Services → their endpoint
pods (dashed; red when not ready), and Deployments → ReplicaSets → Pods → Nodes (StatefulSets,
DaemonSets and Jobs own their pods directly; CronJobs own Jobs). Old ReplicaSets with no pods are
left out. Click a box for its details. ⛶ Full screen (or F) gives the dashboard the whole
screen — the browser’s full screen where it is allowed, otherwise a full-window layer (iPhone) —
with the tabs at the top; on Topology the diagram fills the space and is scaled up to fit as the
window changes (a big namespace keeps its size and scrolls). ◫ Panels hides the connection
panel. Escape leaves.
YAML views drop managedFields unless you tick Show managedFields. Copy command in a detail
panel copies the matching kubectl describe, kubectl rollout status or docker inspect.
Requests lists every call the page makes, newest first. Each can be copied three ways: curl
against the relay, direct — straight to Docker’s socket
(curl --unix-socket /var/run/docker.sock http://localhost/v1.47/containers/json?all=1) or
kubectl get --raw — and as the everyday docker or kubectl command that shows the same
thing (docker logs -f --tail 200 web, kubectl get pods -n shop -o wide). Download as script
saves them all.
What the relay allows
-
GET only.
POST,PUT,PATCH,DELETEandHEADare refused with 405, so nothing can be created, started, stopped, scaled or deleted through it. Streaming reads —docker logs --follow,docker stats,kubectl logs -f, watches — pass through as they arrive. -
An allow-list of read endpoints. Docker: ping, version, info, disk usage, events, and listing and inspecting containers (with logs, stats, top and changes), images (with history), volumes and networks. Not
exec,attach,archiveorexport(which copy files out of a container), orimages/get. Kubernetes: discovery, version and health, OpenAPI, and resources — but not theexec,attach,portforwardandproxysubresources, and not Secrets. -
Not an open proxy. It talks only to the Docker socket and the one API server you named, and refuses paths with
..,//, backslashes or encoded slashes. -
Only this site. A request from a web page carries an
Originheader; onlyhttps://geekychris.github.ioand local previews (http://127.0.0.1:*,http://localhost:*) are answered. Add another with--allow-origin. Tools such as curl send no Origin and are allowed. TheHostheader must name this machine, which stops DNS-rebinding tricks. -
Local. It binds to
127.0.0.1; keep it that way (--hostexists for containers and VMs, not for networks). -
The read-only rules are in the relay, not just the page: anything on your machine that can reach
127.0.0.1:8098can read what the page reads. Stop it (Ctrl+C) when you are done.
The page itself never holds a token or a kubeconfig, and they are never put in links, the
request log or copied commands. Chrome may ask whether this site may reach devices on your local
network: allow it. Safari may refuse http://127.0.0.1 from an HTTPS page; use Chrome, Edge or
Firefox, or a local preview.
Limits
-
Read-only by design: no exec, restarts, scaling, deletes or port forwarding. Use the docker or kubectl commands the request log gives you.
-
Secrets are never read, so their keys do not appear either.
-
Lists are fetched whole (no pagination): fine for a laptop or a small cluster, slow for thousands of pods. Choose a namespace to keep them small.
-
The Stats view samples up to 24 running containers; each sample takes Docker about a second.
-
Pod CPU and memory use need metrics-server and are not shown; node figures are capacity and allocatable, not use.
-
The STATUS column follows kubectl’s rules for what the API returns; a few cases kubectl derives from extra node data (such as
NodeLost) are simplified. -
Logs keep the newest 5,000 chunks in the page.
-
The demo is a recording made with the relay from a few throwaway containers and a one-node k3s cluster (Kubernetes 1.37); its logs replay in a loop and its stats wobble on purpose.
Related: Compose, Kubernetes, and cron checker, Kubernetes manifest builder, kubectl helper, YAML / JSON converter, Kafka client (the same request-log idea for Kafka).
Linking to an example
Share link copies a link with the dashboard’s view: the relay address (without any user,
password or query string), namespace, Docker and Kubernetes views, and filters. A link never
connects by itself — it opens the demo until you press Connect. Put the tab after #:
docker, k8s, events, topology or log.
| Parameter | Meaning | Example |
|---|---|---|
|
the relay’s address (default |
|
|
namespace (empty: all) |
|
|
|
|
|
|
|
|
filter for the Docker and Kubernetes tables |
|
|
events of one type: |
|
|
events text filter |
|
Examples (they open on the demo):
-
?docker=stats#docker— live stats of every running container. -
?ns=shop&k8s=pods#k8s— the pods of namespaceshop. -
?ns=shop&ev=Warning#events— only warnings, such as a crash-looping pod’sBackOff. -
?ns=shop#topology— the topology ofshop.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser — network requests go only to the services named on this page.