Skip to main content

mtop

htop for Ollama — a curses-based TUI that monitors your models, GPU, and the Ollama server (in Docker, Podman or bare-metal) in real time. Zero flicker. Zero dependencies beyond Python 3.10+.

Python 3.10+ License: MIT Platform: Linux

mtop on a DGX Spark: Ollama in Docker, two models loaded, NVML per-process memory, runner config, server environment and the access log

Why?

There are web dashboards, Prometheus exporters, and chat TUIs for Ollama. But there's no terminal monitor — something you SSH into a box and just run, like htop or nvtop, to see what models are loaded, how much VRAM they're eating, and whether the container is healthy.

mtop fills that gap. One file, one command, pure stdlib Python. It auto-detects whether Ollama runs in a Docker/Podman container or as a bare-metal process (systemd or a manual ollama serve) and monitors it either way.

Features

  • Zero-flicker display — curses double-buffered rendering, no clear + print loops
  • Effective inference config — RUNNERS section parses each runner's argv: context, batch, flash attention, KV cache dtype, layer offload. The only place these are observable
  • Whole-tree process stats — the server and its per-model ollama runner subprocesses, with PSS accounting where available
  • Server config — the OLLAMA_* / CUDA_VISIBLE_DEVICES / HSA_* environment the server was started with, from the container, the process, or the systemd unit; next to RUNNERS this is "what was set" vs "what was negotiated"
  • Which model on which card — NVIDIA per-process VRAM via NVML (ctypes, no nvidia-smi fork) joined to the runner PIDs: a GPU column in RUNNERS and a procs: line under each card
  • Loaded models — name, VRAM/RAM split, context length, processor type, TTL countdown
  • Container health — status indicator (●/✗/○), uptime, CPU & memory with progress bars
  • Multi-vendor GPU monitoring — NVIDIA via NVML (nvidia-smi as fallback), AMD and Intel via sysfs, Apple Silicon via ioreg; side by side on the same host; utilization, VRAM/GTT, temperature, power draw
  • AMD without ROCm — telemetry comes from /sys/class/drm/card*/device, so a bare amdgpu driver is enough; rocm-smi is only a fallback
  • Jetson / Tegra / NVIDIA Spark — automatic fallback to unified memory via /proc/meminfo
  • Non-blocking UI — all I/O (docker, nvidia-smi, HTTP) runs in a background collector thread; the interface stays responsive at 100 ms even when the API hangs, and stale data is flagged
  • Interactive — q to quit, +/- to adjust refresh interval, o to toggle raw ollama ps
  • Log panel with request stats — tail of the container log or journalctl, with requests/minute, status classes and p50 latency parsed from Ollama's GIN access log (l to toggle, --logs)
  • Sparklines — CPU, MEM, GPU utilization and VRAM history next to each bar, block characters, stdlib deque
  • Scriptable — --json one-shot mode for cron or Ansible facts (exit code 1 on unhealthy); --json --watch streams NDJSON
  • Prometheus exporter without a port — --prometheus prints text exposition format; --prometheus --watch -o …/textfile/mtop.prom keeps a node_exporter textfile fresh with atomic writes, no cron needed
  • API-only mode — --no-docker for monitoring remote Ollama instances without local docker calls
  • cgroup-aware CPU bar — normalizes against the container's --cpus/quota limit, not the host core count
  • Docker and bare-metal — auto-detects the source: a Docker container, a systemd ollama.service, or a manual ollama serve; monitors process CPU/MEM via /proc (Linux, no root) or ps/sysctl (macOS)
  • Docker Engine API over the socket — no docker binary needed: mtop talks to /var/run/docker.sock (or a rootless / Podman socket, or $DOCKER_HOST) with stdlib http.client; the docker/podman CLI is only a fallback. One-shot stats mean no 2 s docker stats stall per cycle
  • Podman — same code path via the compat API socket or the podman CLI
  • Respects $OLLAMA_HOST — works with remote Ollama instances out of the box
  • Several instances at once — repeat -u; each remote gets its own LOADED MODELS table. Two Ollama instances on a mixed CUDA + ROCm host, or a fleet of remote boxes, on one screen
  • Behind a reverse proxy — -H 'Authorization: Bearer …', $OLLAMA_API_KEY, basic auth in the URL, --insecure / --cacert for private certificates
  • Zero external dependencies — only Python stdlib (curses, urllib, json, subprocess)

Quick Start

One-liner (no install)

curl -fsSL https://github.com/Quaerendir/mtop/releases/latest/download/mtop.py -o mtop.py
chmod +x mtop.py
./mtop.py

pip install

pip install ollama-mtop
mtop

Or without touching the system Python: pipx install ollama-mtop / uv tool install ollama-mtop.

From source

git clone https://github.com/Quaerendir/mtop.git
cd mtop
pip install -e .
mtop

Run directly from a clone (no install)

git clone https://github.com/Quaerendir/mtop.git
cd mtop
PYTHONPATH=src python -m mtop

Usage

mtop [-c CONTAINER] [-i INTERVAL] [-u URL ...] [-H HEADER] [--insecure] [--cacert FILE]
     [-m MODE] [--runtime RT] [--no-gpu] [--json | --prometheus] [--watch] [-o FILE] [-V] [-h]

Options:
  -c, --container NAME   Docker container name (default: ollama)
  -m, --mode MODE        Data source: auto|docker|local|api (default: auto)
                         local = bare-metal `ollama serve` (systemd/proc/ps)
                         api   = models only, no host resource stats
  -i, --interval SECS    Refresh interval in seconds (default: 1.0)
  -u, --api-url URL      Ollama API base URL (default: $OLLAMA_HOST or http://localhost:11434)
                         Scheme-less values (gpu-rig:11434) are accepted, like Ollama itself.
                         Repeatable: the first is the primary (host stats, runners), the
                         rest are API-only. Optional label: -u rig=http://gpu-rig:11434.
                         Credentials in the URL (https://user:pw@host) become basic auth.
  -H, --header 'N: v'    Extra HTTP header for every API request (repeatable);
                         $OLLAMA_API_KEY is sent as 'Authorization: Bearer …' automatically
      --insecure         Skip TLS certificate verification for https:// endpoints
      --cacert FILE      CA bundle (PEM) to verify https:// endpoints against
      --runtime RT       How to reach the container runtime: auto|api|cli (default: auto)
                         api = Engine API on $DOCKER_HOST or a docker/podman socket
                         cli = docker/podman subprocesses
      --no-gpu           Disable GPU monitoring section
      --no-runners       Hide the RUNNERS section (effective inference config)
      --no-env           Hide the SERVER CONFIG section (OLLAMA_* environment)
      --logs             Show the LOGS section from the start (toggle with l); adds
                         `logs` with request stats to --json / --prometheus
      --log-lines N      Log lines to show (default: 8)
      --control          Allow model actions: arrow keys select a loaded model, s stops
                         (unloads) it after a y/N prompt, L loads one from /api/tags.
                         Off by default — without it mtop never writes to the API
      --no-docker        API-only mode: skip all docker calls (remote instances)
      --json             Print one snapshot as JSON and exit (exit 1 on unhealthy)
      --prometheus       Print one snapshot in Prometheus text exposition format and exit
      --watch            With --json/--prometheus: emit every INTERVAL seconds until Ctrl-C
                         (--json --watch prints NDJSON, one compact object per line)
  -o, --output FILE      Write to FILE instead of stdout: Prometheus output replaces the file
                         atomically (tmp + rename), NDJSON is appended
  -V, --version          Show version
  -h, --help             Show help

Examples

# Monitor a custom container name
mtop -c my-ollama

# Slower refresh for remote/metered connections
mtop -i 5

# Bare-metal Ollama (systemd service or `ollama serve` in a terminal)
mtop --mode local

# Let mtop figure it out (docker? systemd? manual? — it probes in that order)
mtop

# Monitor a remote Ollama instance — API only, no local docker/GPU noise
mtop -u 192.168.1.100:11434 --mode api

# Several instances: the local one with full host stats, two remotes API-only
mtop -u localhost:11434 -u rig=gpu-rig:11434 -u spark=192.168.3.6:11434

# Ollama behind a reverse proxy with a private CA and a bearer token
OLLAMA_API_KEY=… mtop -u https://ollama.internal --cacert /etc/ssl/internal-ca.pem

# Podman (rootless): the socket is found automatically; or point at it
DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock mtop

# mtop inside a container, with only the socket mounted — no docker CLI needed
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock \
  --network host python:3.12-slim sh -c \
  "curl -fsSL https://github.com/Quaerendir/mtop/releases/latest/download/mtop.py -o m.py && python m.py"

# One-shot health/state snapshot for scripting (schema_version=1 is the first key;
# it changes only when a field changes meaning or goes away)
mtop --json | jq '.models[].name'

# Stream one JSON line per second (NDJSON) — feed it to jq, a log shipper, or a file
mtop --json --watch -i 1 | jq -c '{t: .wallclock, models: [.models[].name]}'

# Prometheus, no port: keep a node_exporter textfile fresh (atomic writes)
mtop --prometheus --watch -i 15 -o /var/lib/node_exporter/textfile_collector/mtop.prom

# Using OLLAMA_HOST environment variable
export OLLAMA_HOST=http://gpu-rig:11434
mtop

Interactive Keys

Key Action
q / ESC Quit
+ Decrease refresh interval (faster)
- Increase refresh interval (slower)
o Toggle raw ollama ps section
r Toggle the RUNNERS section
e Toggle the SERVER CONFIG section
l Toggle the LOGS section (container logs / journalctl, request stats)
↑ / ↓ Select a loaded model (--control only)
s Stop (unload) the selected model after a y/N prompt (--control only)
L Pick a model from /api/tags and load it; Tab switches endpoint. Asks first when the server is at OLLAMA_MAX_LOADED_MODELS (--control only)

What you see

The screenshot above is a real session on a DGX Spark (GB10) with Ollama in Docker and two models loaded. Top to bottom:

  • header — host, container (or ollama: serve · pid, or the API URL in api mode), uptime, the Ollama version, and +N endpoints when several are polled; STALE appears if the collector falls behind
  • CONTAINER / PROCESS RESOURCES — CPU normalized to the cgroup / systemd / affinity budget, memory with the accounting named (pss, rss or cgroup), sparklines of the recent history
  • GPU — one block per card, any vendor; procs: lists what NVML sees on the card, joined to the runners by PID
  • LOADED MODELS — /api/ps, one table per endpoint when there are several; PROCESSOR computed exactly as ollama ps does
  • RUNNERS — the negotiated inference config from each runner's argv, plus the card it sits on
  • SERVER CONFIG — the OLLAMA_* environment the server was started with and where it was read from
  • LOGS — tail of the container log or journal, with request rate and latency parsed from the GIN access log
  • footer — keys, the container runtime in use (via docker-api), version

o adds the raw ollama ps table. Refresh the screenshot with tools/screenshot.py (see its docstring).

Supported Platforms

Platform GPU Monitoring Notes
Linux x86_64 + NVIDIA ✅ Full NVML via ctypes; nvidia-smi on host or in container as fallback
NVIDIA Jetson / Orin ✅ Unified memory Falls back to /proc/meminfo
NVIDIA GB10 Spark ✅ Unified memory Tegra-based, same fallback
Linux + AMD (amdgpu) ✅ Full sysfs — no ROCm install required
AMD APU (780M, Strix) ✅ Unified memory GTT pool, not the tiny VRAM carve-out
Linux + Intel (Arc / Xe / iGPU) ⚠️ Partial, untested sysfs: name, VRAM total (xe), temperature, power, frequency; no utilization without root
Linux without GPU ✅ (no GPU section) Use --no-gpu to hide the section
Ollama in Podman ✅ Full compat API on the Podman socket, or the podman CLI
Bare-metal Ollama (systemd) ✅ process stats --mode local; CPU/MEM from /proc, no root needed
Manual ollama serve ✅ process stats auto-detected via /proc cmdline scan
macOS (Apple Silicon) ✅ Unified memory (no temp/power) Verified on M5 Pro, macOS 26.6: GPU utilization and in-use memory via ioreg, no root; --mode local process stats via ps/sysctl; temperature/power need root (powermetrics)
WSL2 ⚠️ Partial Works if Docker + nvidia-container-toolkit configured

Requirements

  • Python 3.10+ (uses match-era type hints like list[str], X | Y)
  • Docker or Podman for container monitoring — access to the socket is enough, the CLI is optional
  • Ollama in a container, as a bare-metal process, or reachable via API
  • NVIDIA driver (optional, for GPU stats — libnvidia-ml.so.1, or nvidia-smi as fallback)

Roadmap

  • Record terminal sessions with asciinema for README gif
  • AMD GPU support (sysfs first, rocm-smi fallback)
  • Apple Silicon GPU stats (via ioreg, no root — powermetrics needs sudo)
  • Intel GPU stats via sysfs (partial: no utilization without CAP_PERFMON)
  • Model pull progress tracking
  • Multi-host support (repeatable -u, per-endpoint model tables)
  • Docker Engine API over the socket, Podman support
  • Configurable layout (raw ollama ps toggle; more sections to follow)
  • Model actions — unload on keypress and load from /api/tags (--control)
  • Model actions — extend TTL
  • Sparkline history for CPU/GPU utilization (block chars, stdlib deque)
  • systemd/bare-metal Ollama support (process stats via /proc, no Docker required)
  • Log panel (container logs / journalctl) with request stats from the GIN log
  • Effective inference config per runner (context, flash attention, KV dtype)
  • Prometheus text exposition (--prometheus) and NDJSON streaming (--watch)
  • Request rate from the access log (tokens/s is per-response data Ollama returns only to the caller — not observable from outside)
  • Effective server environment (OLLAMA_*) per source
  • Runner → GPU mapping via NVML per-process memory

Contributing

PRs welcome. Keep it stdlib-only — the zero-dependency constraint is a feature, not a limitation.

git clone https://github.com/Quaerendir/mtop.git
cd mtop
pip install -e ".[dev]"      # + pytest, ruff
# hack on src/mtop/*.py — util, procfs, runner, collector, ui, cli, container, gpu, logs, export
mtop

ruff check src tools tests
pytest                       # stdlib-only fakes: no GPU, docker or Ollama needed

# regenerate the single-file artifact shipped with releases
python tools/bundle.py       # -> dist/mtop.py

dist/mtop.py is generated — never edit it by hand. The version lives in src/mtop/_version.py only. A v* tag attaches the bundle to the GitHub release and, once the repository variable PYPI_TRUSTED is set, publishes the sdist and wheel to PyPI through Trusted Publishing (GitHub OIDC, no token in the repo). The tag must equal v + the package version.

License

MIT — see LICENSE.

Acknowledgements

Built as a collaboration between a human homelab geek and Claude (Anthropic) during a late-night infrastructure session. The original bash prototype migrated to Python/curses because fighting tput and jq in a loop was getting old.

Metadata

Release files for ollama-mtop 0.12.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ollama-mtop 0.12.0
File Size Uploaded Provenance Actions
ollama_mtop-0.12.0.tar.gz 508.4 kB With provenance Details for ollama_mtop-0.12.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ollama-mtop 0.12.0
File Interpreter ABI Platform Actions
ollama_mtop-0.12.0-py3-none-any.whl Python 3 none any Details for ollama_mtop-0.12.0-py3-none-any.whl

Total release size: 589.2 kB

Release files / ollama_mtop-0.12.0.tar.gz

Download URL ollama_mtop-0.12.0.tar.gz
Size 508.4 kB
Tags Source
SHA-256 checksum
How to use checksums
20eda2e4ae7b8df25bb9419e519813ef3820db5f59ab113fe11db3d0cd93296f
BLAKE2b-256 checksum
How to use checksums
24eccc266e3f4bcef5f761aeb725e195631fdfb0bcda695fa1768156bd417e3c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / ollama_mtop-0.12.0-py3-none-any.whl

Download URL ollama_mtop-0.12.0-py3-none-any.whl
Size 80.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
70c14fcd75a3d2238708fd1647d7f28b01ad7eee8fff04daeb427b6ec1bbc42a
BLAKE2b-256 checksum
How to use checksums
5c072685d3b040999daa8b3e4ec755627e744f6141ef8455e9c6abaa99052a71
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Security for ollama-mtop 0.12.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ollama_mtop-0.12.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page