lcars-k8s is a Kubernetes operations dashboard with two interfaces:
- A pixel-rendered LCARS dashboard for a local display
- A responsive Textual terminal interface for SSH, Kmscon, FbTerm, and ordinary terminals
It displays cluster CPU and memory history, node utilization, pods, deployments, events, container logs, Ingress NGINX request logs, pod details, and Kubernetes manifests. The dashboard is strictly read-only.
Requirements
Supported platform
The installer targets Ubuntu Server 22.04 and 24.04. The application requires:
- Python 3.10 or newer
- Network access to Ubuntu package repositories and PyPI during installation
sudoaccess when a required Ubuntu package is missing- A usable kubeconfig for a real cluster, or the included demo data source
- An active local kernel VT for fullscreen graphical mode
The terminal interface also works over SSH. A desktop environment is not required for fullscreen graphical mode.
Dependencies installed automatically
Running ./install.sh installs missing Ubuntu system packages:
python3python3-venvxinitxserver-xorg-core
It then creates a private virtual environment and installs these Python runtime
dependencies from pyproject.toml:
pygame-cetextualkubernetes
Kmscon and FbTerm are optional terminal hosts. The installer does not install them.
Installation
1. Enter the project directory
If you downloaded an archive, extract it and enter the directory containing
install.sh:
If necessary, make the installer executable:
Do not run the entire installer with sudo. It uses sudo only when it needs
to install a missing Ubuntu package.
2. Run the installer
The installer performs these steps:
- Installs missing Python and Xorg packages with
apt-get. - Verifies that Python 3.10 or newer is available.
- Creates
~/.local/share/lcars-k8sas a private virtual environment. - Installs lcars-k8s and its Python dependencies into that environment.
- Creates
~/.local/bin/lcars-k8sas a symbolic link to the command.
The installer replaces an existing lcars-k8s virtual environment when rerun. It does not install packages into the system Python environment.
3. Ensure the command is on PATH
Ubuntu normally includes ~/.local/bin after a new login. For the current
shell, use:
export PATH="$PATH:$HOME/.local/bin"
To add it permanently when your shell does not already include it:
printf '\nexport PATH="$PATH:$HOME/.local/bin"\n' >> "$HOME/.bashrc" source "$HOME/.bashrc"
4. Verify the installation
lcars-k8s --version lcars-k8s --demo
The demo uses a synthetic cluster and does not need Kubernetes credentials.
Press q to exit.
Alternative pipx installation
If Python, pipx, and the required Xorg packages are already installed:
A pipx installation does not install the Xorg system packages. Install them separately before using fullscreen graphical mode:
sudo apt-get update sudo apt-get install -y xinit xserver-xorg-core
Connecting to Kubernetes
Use the current kubeconfig context
The default command uses the current context from the standard kubeconfig:
kubectl config current-context kubectl cluster-info lcars-k8s
The Kubernetes Python client searches the normal kubeconfig location. You can
also set KUBECONFIG:
export KUBECONFIG="$HOME/.kube/config" lcars-k8s
Select a kubeconfig and context explicitly
lcars-k8s --kubeconfig /path/to/config --context production
Start in one namespace when desired:
lcars-k8s --namespace kube-system
The n key cycles through namespaces after startup. The a key returns to all
namespaces.
Required Kubernetes permissions
The active Kubernetes identity needs these permissions:
| API resource | Verbs | Purpose |
|---|---|---|
| nodes | get, list | node status and capacity |
| pods | get, list | pod status, details, and manifests |
| pods/log | get | container logs |
| events | get, list | event view |
| deployments.apps | get, list | deployment readiness and rollout status |
| namespaces | get, list | namespace selection |
| metrics.k8s.io nodes and pods | get, list | live CPU and memory usage |
Check the important permissions for the current identity:
kubectl auth can-i list nodes kubectl auth can-i list pods --all-namespaces kubectl auth can-i get pods/log --all-namespaces kubectl auth can-i list events --all-namespaces kubectl auth can-i list deployments.apps --all-namespaces kubectl auth can-i list nodes.metrics.k8s.io
rbac.yaml creates a service account, ClusterRole, and ClusterRoleBinding with
the required permissions:
kubectl apply -f rbac.yaml
When lcars-k8s runs inside a Kubernetes pod, it automatically uses the pod's
service account if no usable kubeconfig is available. Set the pod spec's
serviceAccountName to lcars-k8s after applying rbac.yaml.
Metrics Server
Live usage requires Metrics Server. Verify it independently:
kubectl top nodes kubectl top pods --all-namespaces
If Metrics Server is unavailable, lcars-k8s continues using summed resource requests. The status area indicates that allocation data is being shown instead of live usage.
Running the terminal interface
Run against the current Kubernetes context:
Useful examples:
lcars-k8s --demo lcars-k8s --context production lcars-k8s --namespace kube-system lcars-k8s --interval 5 lcars-k8s --timeout 20 lcars-k8s --view nodes lcars-k8s --view network lcars-k8s --view network --network-status 404 --network-path /api/ lcars-k8s --view network --network-exclude /healthz --network-exclude metrics
The terminal layout adapts down to 80 columns by 24 rows. A terminal with at least 120 columns by 40 rows is recommended. For the full palette and braille graphs, use a terminal with truecolor and a font that contains Unicode braille and block glyphs.
Over SSH, screen, or tmux, a suitable environment is:
export TERM=xterm-256color
lcars-k8sUse COLORTERM=truecolor only when the terminal actually supports 24-bit
color.
Linux virtual console
The Linux virtual console supports only a limited palette and lacks many of the
required glyphs. lcars-k8s detects TERM=linux and selects its console palette
and solid graphs automatically.
You can select that mode explicitly:
lcars-k8s --colors console --glyphs solid
Available glyph profiles are:
| Profile | Intended use |
|---|---|
braille |
modern terminal with complete Unicode fonts |
block |
terminal with block glyphs but no braille support |
solid |
Linux virtual console fonts |
ascii |
maximum compatibility |
auto |
follows the detected color mode |
Color and glyph settings are independent. For example:
lcars-k8s --colors console --glyphs braille
Running fullscreen graphical mode
The graphical renderer draws a 1920x1080 LCARS composition and scales it to the
active display. Normal fullscreen mode starts a private Xorg server with
xinit; it does not require a desktop environment.
Before launching
- Log in directly on the machine using a local kernel VT.
- Stop or leave any Kmscon, desktop compositor, or X server that owns that VT.
- Confirm that
xinitandXorgare installed. - Run the command in the foreground.
Check the required commands:
command -v xinit command -v Xorg
Launch with demo data first:
lcars-k8s --graphics --demo
Then connect to the real cluster:
The launcher clears inherited DISPLAY and WAYLAND_DISPLAY values, selects
SDL's X11 backend, starts Xorg on display :1, and binds Xorg to the active VT
when the terminal can be identified.
Do not start fullscreen graphical mode as a background SSH job. Xorg needs an active local session and control of a local VT. Every segment in the top and bottom signal rails operates independently. Each segment holds its current lit or dim state for a random interval between three and six seconds, then toggles and chooses another duration. The six independent timers create an unpredictable lighting order without synchronizing the rails.
Desktop window preview
Under an existing X11 or Wayland desktop session, use windowed mode:
lcars-k8s --graphics --windowed --demo
Specify the initial window size when needed:
lcars-k8s --graphics --windowed --resolution 1600x900 --demo
The resolution must use the WIDTHxHEIGHT form.
Direct KMS diagnostic mode
Raw SDL KMSDRM remains available for hardware diagnostics:
lcars-k8s --graphics --direct-kms --demo
This is not the recommended normal mode. Display and keyboard support depends on the SDL build, DRM device, active session, and GPU driver. A black screen or missing keyboard input can leave the process running.
Recover from another shell or SSH session with:
pgrep -af 'Xorg|xinit|lcarsk8s|lcars-k8s' pkill -TERM -f '/lcars-k8s/bin/python3 -m lcarsk8s'
Inspect the process list before terminating anything so that an unrelated Xorg session is not stopped.
Graphical diagnostics
The graphical renderer writes the first completed design frame to:
/tmp/lcars-k8s-live-frame.png
Capture startup output for troubleshooting:
rm -f /tmp/lcars-graphics.log /tmp/lcars-k8s-live-frame.png lcars-k8s --graphics --demo > /tmp/lcars-graphics.log 2>&1
After a failed Xorg launch, inspect:
cat /tmp/lcars-graphics.log find "$HOME/.local/share/xorg" /var/log -maxdepth 2 -name 'Xorg*.log' -type f 2>/dev/null
The newest Xorg log normally identifies VT ownership, GPU selection, device permission, display lock, or driver problems.
Optional Kmscon terminal host
Kmscon provides a local terminal with 256 colors, Pango font rendering, UTF-8, and KMS/DRM output. Install it separately if desired:
sudo apt-get update sudo apt-get install -y kmscon fonts-dejavu-core
The supplied configuration selects DejaVu Sans Mono and an LCARS-oriented 256-color palette. Review an existing configuration before replacing it:
sudo mkdir -p /etc/kmscon sudo cp kmscon-lcars.conf /etc/kmscon/kmscon.conf
Start lcars-k8s from a shell hosted by Kmscon:
lcars-k8s --kmscon --demo lcars-k8s --kmscon
The --kmscon option selects the application's Kmscon display profile. It does
not launch Kmscon itself. Do not set COLORTERM=truecolor; Kmscon advertises a
256-color terminal.
Kmscon must release the active display before fullscreen graphical mode starts.
Optional FbTerm terminal host
FbTerm provides another 256-color local terminal with Freetype font rendering. Install and start FbTerm separately, then run:
lcars-k8s --fbterm --demo lcars-k8s --fbterm
The --fbterm option sets the correct application profile and TERM=fbterm.
It does not launch FbTerm. A useful ~/.fbtermrc font configuration is:
font-names=DejaVu Sans Mono font-size=16
Do not set COLORTERM=truecolor for FbTerm.
Keyboard controls
| Key | Action |
|---|---|
1 |
show or hide cluster graphs |
2 |
open pods |
3 |
open nodes |
4 |
open events |
5 |
open deployments |
6 |
open the accumulated Ingress NGINX network log |
Tab |
cycle through views |
n |
select the next namespace |
a |
show all namespaces |
/ or f |
filter the current view |
Esc |
clear or close the current filter or modal |
< or > |
change the pod sort column |
r |
reverse pod sort order |
| arrow keys | move the selected row or scroll a modal |
Page Up or Page Down |
move through graphical rows in larger steps |
Home or End |
select the first or last graphical row |
l |
open the selected pod's container log |
d |
open pod details and manifest |
m |
toggle the manifest in the terminal detail modal |
c |
select the next container in the terminal log modal |
p |
include previous container logs in the terminal log modal |
Space |
hold or resume scanning |
+ |
increase the interval and scan less often |
- |
decrease the interval and scan more often |
Ctrl+R |
scan immediately |
F5 |
scan immediately in graphical mode |
? |
open help |
q |
quit or close the active modal |
Command line reference
| Option | Description |
|---|---|
--demo |
use synthetic cluster data |
--kubeconfig PATH |
use a specific kubeconfig file |
--context NAME |
use a specific kubeconfig context |
-n NAME, --namespace NAME |
start in one namespace |
-i SECONDS, --interval SECONDS |
set the scan interval, default 5 seconds, minimum 0.5 |
--view pods or --view 2 |
start in the pods view |
--view nodes or --view 3 |
start in the nodes view |
--view events or --view 4 |
start in the events view |
--view deployments or --view 5 |
start in the deployments view |
--view network or --view 6 |
start in the Ingress NGINX network log view |
--network-status STATUS |
include one HTTP status; repeat to include several |
--network-ingress TEXT |
include ingress names containing the text |
--network-path TEXT |
include request paths containing the text |
--network-exclude TEXT |
hide rows whose ingress or path contains the text; repeat for more |
--timeout SECONDS |
set the API request timeout, default 10 seconds |
--graphics |
use the pixel-rendered SDL interface |
--windowed |
use a resizable desktop window with graphical mode |
--direct-kms |
use SDL KMSDRM instead of the Xorg kiosk |
--resolution WIDTHxHEIGHT |
set graphical output resolution |
--sidebar left or --sidebar right |
place navigation on either side |
--no-graphs |
start with CPU and memory graphs hidden |
--kmscon |
use the Kmscon terminal profile |
--fbterm |
use the FbTerm terminal profile |
--colors auto |
detect the terminal color mode |
--colors full |
force the full LCARS palette |
--colors console |
force the hand-selected 16-color palette |
--glyphs PROFILE |
select auto, braille, block, solid, or ascii |
--version |
print the installed version |
-h, --help |
show built-in help |
Display modifiers such as --windowed, --direct-kms, and --resolution are
intended to be used with --graphics.
The network view discovers controller pods by the standard Ingress NGINX labels,
with a controller-name fallback. Each poll reads the previous 60 seconds from
every discovered replica, parses default-format or JSON access logs, and merges
new requests into a deduplicated session history. The newest 500 requests are
retained. Graphical mode preserves existing rows between refreshes and reveals
only newly received requests across the five-second polling cycle. Requests are
shown in chronological order with new entries added at the bottom. Once the
table is full, each new entry removes the oldest row from the top to create a
scrolling log. At a random interval between 30 seconds and two minutes, the
visible network table clears once and rebuilds over five seconds. Polling does
not otherwise clear the table. Terminal mode allows scrolling through the
retained history. Graphical network row backgrounds use periwinkle
for 2xx, lavender for 3xx, gold for 4xx, red for 5xx, and grey for other status
families, keeping them distinct from the ice table header. Row text is black,
and the selected row has a white outline. For default-format logs, the ingress
column shows the upstream name. Press / or f to filter the visible IP, status, ingress or
upstream name, or path. To hide noisy traffic such as health checks or
services that poll every few seconds, start with --network-exclude TEXT; any
request whose ingress or path contains the text is dropped from the table, and
the table bar lists the active excludes. A 24H HITS panel beside the log
totals requests per ingress over the last 24 hours, busiest first, leaving out
anything matched by --network-exclude. The Kubernetes logs API only serves a
controller's current log file, so lcars-k8s keeps the count itself: once a
minute, on every view, it reads the controller logs written since its last read
and adds each request once, in per-minute buckets that roll off after 24 hours.
Counts are saved to ~/.cache/lcars-k8s/hits-<context>.json, and on start it
catches up on requests made while it was closed, as far back as the
controller's current log file reaches. Until lcars-k8s has been running for a
day, the panel covers less than 24 hours. If Ingress NGINX is absent or inaccessible, the view
remains available and reports the condition in the status area.
The verified JSON format uses time, status, ingress, and path:
{"time":"2026-09-12T04:03:33+00:00","ip":"172.16.46.24","method":"GET","path":"/wp-admin/install.php","status":404,"bytes":4333,"rt":0.003,"ingress":"default-www-depletement-com-80","upstream":"172.16.228.125:4321","ustatus":"404","urt":"0.003"}The parser also accepts timestamp or time_iso8601 for time,
ingress_name, resource_name, or proxy_upstream_name for ingress, and
request_uri, uri, or the path in request for path.
Troubleshooting
No usable kubeconfig
If the application reports that there is no usable kubeconfig and it is not running inside a pod:
kubectl config current-context kubectl cluster-info
Then set KUBECONFIG or pass --kubeconfig explicitly.
Permission errors
Use kubectl auth can-i with the same context and identity used by lcars-k8s.
Apply rbac.yaml only when its cluster-wide service account permissions match
your security requirements.
Empty network view
Confirm that the controller can be discovered:
kubectl get pods -A \ -l app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/component=controller
Confirm log permission and inspect the same one-minute fetch window used by each dashboard poll. Replace the namespace and pod name with values from the first command:
kubectl auth can-i get pods/log -n ingress kubectl logs -n ingress ingress-nginx-controller-example \ -c controller --since=1m
Generate a request through an ingress and scan again if the log command is
empty. Once received, a request remains in the session history until it falls
outside the newest 500 retained entries or the application exits. Clear any
active view filter with Esc. The status area distinguishes an undiscovered
controller, a log permission or read failure, and a session that has not yet
received requests.
Missing metrics
Run kubectl top nodes. If that fails, repair or install Metrics Server. The
dashboard remains operational using resource requests.
Large clusters or slow API responses
Increase the scan interval and request timeout:
lcars-k8s --interval 10 --timeout 30
Start within one namespace if an all-namespace pod list is unnecessarily large:
lcars-k8s --namespace production
Incorrect colors or replacement glyphs
For the Linux virtual console:
lcars-k8s --colors console --glyphs solid
For an ordinary modern desktop terminal:
lcars-k8s --colors full --glyphs braille
If braille or partial blocks appear as diamonds or empty boxes, use a font with
complete Unicode braille and block coverage, such as DejaVu Sans Mono, or select
--glyphs solid.
Xorg reports an active display
A compositor, Kmscon instance, or stale X server still owns the display or VT. Switch to an unused local VT or stop only the stale dashboard-related process, then retry in the foreground.
Graphical startup stops while creating the SDL surface
Normal graphical mode forces SDL's X11 backend because pygame-ce display creation can block on some Wayland and wlroots combinations. Confirm that the normal command is being used and inspect the captured Xorg and application logs.
Updating
From an updated source directory, rerun:
The installer replaces the private virtual environment and refreshes the command link.
Uninstalling
Remove the command and private virtual environment:
rm -f "$HOME/.local/bin/lcars-k8s" rm -rf "$HOME/.local/share/lcars-k8s"
The installer does not track ownership of shared Ubuntu packages, so it does not remove Python or Xorg packages automatically.
Development checks
Development helpers run the interfaces headlessly:
python3 dev/graphics_preview.py python3 dev/graphics_stress.py python3 dev/preview.py shot 150x42 python3 dev/stress.py python3 dev/resize.py
The terminal PNG preview helper also requires CairoSVG:
python3 -m pip install cairosvg
See dev/README.md for helper arguments and expected output.
Project layout
| Path | Purpose |
|---|---|
lcarsk8s/cluster.py |
Kubernetes API and demo data sources |
lcarsk8s/graphics.py |
pixel-rendered pygame interface |
lcarsk8s/app.py |
Textual application, polling, and actions |
lcarsk8s/widgets.py |
terminal dashboard widgets |
lcarsk8s/screens.py |
terminal modals for help, logs, and details |
lcarsk8s/palette.py |
LCARS terminal palettes and load gradients |
lcarsk8s/glyphs.py |
terminal graphs, meters, elbows, and pills |
lcarsk8s/assets/Antonio.ttf |
bundled LCARS title and navigation typeface |
lcarsk8s/assets/AtkinsonHyperlegibleNext-Regular.ttf |
bundled accessible data typeface |
rbac.yaml |
optional Kubernetes service account and permissions |
kmscon-lcars.conf |
optional Kmscon font and palette configuration |
install.sh |
Ubuntu dependency and private environment installer |
dev/ |
headless preview and stress helpers |
The graphical and terminal renderers share the same Kubernetes data layer.
License
The application is licensed under the MIT License in LICENSE. The bundled
Antonio and Atkinson Hyperlegible Next typefaces are distributed under the SIL
Open Font License. Their licenses are in lcarsk8s/assets/Antonio-OFL.txt and
lcarsk8s/assets/AtkinsonHyperlegibleNext-OFL.txt.
