A system monitor for your browser, in one small binary
๐ฆ Latest release
Contents
curl -fsSL https://raw.githubusercontent.com/NotAFlightRisk/peng-top/main/scripts/install.sh | shThat grabs the latest release for your OS and CPU, checks it against the release's checksums and drops peng-top into /usr/local/bin (or ~/.local/bin if that's not writable). Set PENGTOP_INSTALL_DIR to put it somewhere else.
In PowerShell:
irm https://raw.githubusercontent.com/NotAFlightRisk/peng-top/main/scripts/install.ps1 | iexIt installs to %LOCALAPPDATA%\peng-top and adds that to your user PATH, so open a fresh terminal afterwards.
docker run -d --name peng-top --pid host --network host -v /:/host:ro,rslave ghcr.io/notaflightrisk/peng-topThen open localhost:7340. Those three flags are what let it see the host rather than its own container: --pid host for the processes, --network host for the network, and the read-only / mount for disks, sensors and the rest. Without the mount it still runs, it just shows the container (and says so in the top bar). Without --network host the network numbers are the container's, and the System tab says so.
Prefer Compose? There's a ready-made docker/compose.yaml:
curl -fsSLO https://raw.githubusercontent.com/NotAFlightRisk/peng-top/main/docker/compose.yaml
docker compose up -dThe image is also on DockerHub as notaflightrisk/peng-top, for amd64, arm64 and arm/v7. On Docker Desktop (macOS and Windows) the "host" is Docker's own VM, so you'll want the binary there instead.
Every release has an archive per platform, plus checksums.txt, a Sigstore signature for it and an SBOM per archive. Download the one for your machine, unpack it and run peng-top. To check it's ours:
sha256sum --ignore-missing -c checksums.txt
cosign verify-blob checksums.txt \
--bundle checksums.txt.sigstore.json \
--certificate-identity-regexp '^https://github.com/NotAFlightRisk/peng-top/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comYou'll need Go 1.26+ and Node 22+.
git clone https://github.com/NotAFlightRisk/peng-top.git
cd peng-top
make build
./peng-topmake build builds the web UI and bakes it into a single ./peng-top. For working on it, make dev runs the server and npm --prefix web run dev runs the UI with hot reload (it proxies /api through to the server). make test and make lint do what you'd expect.
Run peng-top and open the link it prints, localhost:7340 by default. Ctrl+C stops it.
Everything's driven from the keyboard if you like: 1-8 jump between tabs, / filters processes, p pauses, t cycles themes and ? shows the rest. Settings (theme, units, what's on the overview and so on) are behind s, and they're saved in your browser.
The raw numbers are there too, if you'd rather script it:
curl -s localhost:7340/api/snapshot # latest reading, as JSON
curl -s localhost:7340/api/info # the machine, plus anything it couldn't read
curl -sN localhost:7340/api/stream # server-sent events, one reading per tickEvery option is a flag, or an env var with the same name in capitals and a PENGTOP_ prefix. Flags win.
| Flag | Env var | Default | What it does |
|---|---|---|---|
--host |
PENGTOP_HOST |
127.0.0.1 |
Address to listen on. 0.0.0.0 to open it up to your network |
--port |
PENGTOP_PORT |
7340 |
Port to listen on |
--interval |
PENGTOP_INTERVAL |
2s |
How often to take a reading, 500ms at the quickest |
--history |
PENGTOP_HISTORY |
150 |
How many readings the graphs start with when you open the page |
--auth |
PENGTOP_AUTH |
Ask for a login, as user:password |
|
--allowed-hosts |
PENGTOP_ALLOWED_HOSTS |
Extra hostnames it'll answer to, comma separated, or * for any |
|
--host-root |
PENGTOP_HOST_ROOT |
Where the host's / is mounted, in a container. The image sets /host |
|
--log-level |
PENGTOP_LOG_LEVEL |
info |
debug, info, warn or error |
--no-color |
PENGTOP_NO_COLOR |
Plain logs. NO_COLOR works too |
|
--open |
PENGTOP_OPEN |
Open it in your browser once it's up |
peng-top --help lists them, --version prints the version, and --healthcheck checks a running copy answers (that's what the Docker image's health check uses).
Anyone who can reach it can see your process list, command lines included, so think before putting it on 0.0.0.0 without --auth. Out of the box it only answers to localhost, IP addresses and your machine's own name, which stops a dodgy website reaching it through your browser. If you put it behind a reverse proxy or a hostname like nas.lan, add that with --allowed-hosts (setting --auth turns the check off, since a login does the same job).
On Linux with systemd, save this as ~/.config/systemd/user/peng-top.service:
[Unit]
Description=peng-top
[Service]
ExecStart=%h/.local/bin/peng-top
Restart=on-failure
[Install]
WantedBy=default.targetThen systemctl --user enable --now peng-top, plus sudo loginctl enable-linger $USER if you want it running while you're logged out. Swap the ExecStart path for /usr/local/bin/peng-top if that's where it went. On a box that mounts /proc with hidepid, it'll only see your own processes unless it runs as root.
On macOS a login item does the same job, and on Windows a Task Scheduler task set to run at log on.
"no temperature sensors here" is normal in VMs and a lot of cloud boxes, there just aren't any. The System tab lists everything it couldn't read and why.
"peng-top doesn't know ..." means you've reached it by a hostname it doesn't recognise, see --allowed-hosts above.
The top bar says "container" when you wanted the host: the /:/host mount is missing. If it says "host via container" but the network looks wrong, add --network host.
Something else? Run it with --log-level debug and open an issue with what it prints.
Licensed under MIT, ยฉ NotAFlightRisk 2026
