bcicen/ctop
Top-like interface for container metrics
Stale — last commit 2y ago
worst of 4 axeslast commit was 2y ago; no tests detected…
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
last commit was 2y ago; no CI workflows detected
- ✓14 active contributors
- ✓Distributed ownership (top contributor 45% of recent commits)
- ✓MIT licensed
Show 3 more →Show less
- ⚠Stale — last commit 2y ago
- ⚠No CI workflows detected
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Mixed → Healthy if: 1 commit in the last 365 days; add a test suite
- →Deploy as-is Mixed → Healthy if: 1 commit in the last 180 days
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/bcicen/ctop)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/bcicen/ctop on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: bcicen/ctop
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/bcicen/ctop shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Stale — last commit 2y ago
- 14 active contributors
- Distributed ownership (top contributor 45% of recent commits)
- MIT licensed
- ⚠ Stale — last commit 2y ago
- ⚠ No CI workflows detected
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live bcicen/ctop
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/bcicen/ctop.
What it runs against: a local clone of bcicen/ctop — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in bcicen/ctop | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 700 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of bcicen/ctop. If you don't
# have one yet, run these first:
#
# git clone https://github.com/bcicen/ctop.git
# cd ctop
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of bcicen/ctop and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "bcicen/ctop(\\.git)?\\b" \\
&& ok "origin remote is bcicen/ctop" \\
|| miss "origin remote is not bcicen/ctop (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "main.go" \\
&& ok "main.go" \\
|| miss "missing critical file: main.go"
test -f "connector/main.go" \\
&& ok "connector/main.go" \\
|| miss "missing critical file: connector/main.go"
test -f "container/main.go" \\
&& ok "container/main.go" \\
|| miss "missing critical file: container/main.go"
test -f "grid.go" \\
&& ok "grid.go" \\
|| miss "missing critical file: grid.go"
test -f "connector/collector/docker.go" \\
&& ok "connector/collector/docker.go" \\
|| miss "missing critical file: connector/collector/docker.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 700 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~670d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/bcicen/ctop"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
ctop is a top-like terminal UI for monitoring Docker and runC container metrics in real-time. It displays CPU, memory, network, and I/O statistics for multiple containers simultaneously in a grid layout, with the ability to drill down into individual container details. Written in Go, it serves as a lightweight alternative to docker stats with an interactive, sortable interface. Modular architecture organized by concern: connector/ handles container runtime abstraction (Docker, runC, mock implementations for testing), collector/ gathers metrics, container/ manages container state and sorting, cwidgets/compact/ handles terminal UI rendering, and config/ manages user configuration. Main entry points use these packages to orchestrate metric collection and display.
👥Who it's for
DevOps engineers, SREs, and system administrators who need quick visibility into container resource consumption across multiple containers without opening multiple terminal windows or dashboards. It's particularly valuable for developers running Docker locally who want to debug performance issues during development.
🌱Maturity & risk
Actively maintained and production-ready. The project shows consistent releases (v0.7.7 visible in README), has CircleCI CI/CD configured (.circleci/config.yml), and includes connectors for both Docker and runC. The ~122KB of Go code and structured package organization suggest a mature, stable codebase. No signs of abandonment.
Relatively low risk—single primary maintainer (bcicen) means bus-factor concerns, but the tool's focused scope and stable dependency list (go-dockerclient for Docker, opencontainers/runc for runC) reduce volatility. Dependencies are relatively stable Go libraries (gizak/termui for UI, fsouza/go-dockerclient for container runtime). Last visible activity appears recent based on VERSION file and release strategy.
Active areas of work
Unable to determine exact current work from provided file structure, but the presence of debug documentation (_docs/debug.md), multiple connector implementations (Docker, runC, mock), and recent versioning (VERSION file) indicates active maintenance. The codebase structure suggests recent refactoring toward better abstraction (separate manager/ and collector/ packages).
🚀Get running
git clone https://github.com/bcicen/ctop.git
cd ctop
make build
./ctop
Or using Docker: docker run --rm -ti --volume /var/run/docker.sock:/var/run/docker.sock:ro quay.io/vektorlab/ctop:latest
Daily commands:
make build # Compiles binary to bin/ctop
./ctop # Run with Docker socket auto-detection
ctop -f 'filter' # Run with initial filter
ctop -s memory # Sort by memory by default
ctop -i # Invert colors
🗺️Map of the codebase
main.go— Application entry point that initializes the UI event loop, container monitoring, and grid/single view management—foundational to understanding ctop's execution flow.connector/main.go— Abstraction layer for container data sources (Docker, runC, mock); all metric collection flows through here, making it essential for adding new container backends.container/main.go— Core container model holding metrics state; understanding this data structure is critical for extending metrics capture and display logic.grid.go— Grid view rendering and container selection logic; handles the primary multi-container dashboard display that users interact with.connector/collector/docker.go— Docker metric collection implementation; largest and most commonly used connector that queries the Docker daemon for real-time container stats.cwidgets/compact/grid.go— Compact grid widget responsible for layout and terminal rendering of container metrics in the main view.keys.go— Global keyboard input bindings and command dispatch; required reading for understanding interactive features and control flow.
🧩Components & responsibilities
- main.go (Event Loop) (termbox-go, goroutines) — Core event dispatcher; polls terminal input, triggers connector updates, delegates to grid/single view, manages app lifecycle
- Failure mode: Unresponsive UI; keyboard input blocked; app hangs on connector timeout
- connector/ (Connector Layer)* (go-dockerclient, runc, syscalls) — Abstraction over container runtime APIs; manages discovery, lifecycle, and metric polling; swappable backends
- Failure mode: Metrics not collected; containers not listed; crashes if Docker daemon unavailable or socket permissions denied
- container/ (Data Model)* — Immutable container struct holding all metrics and metadata; sorting/filtering
🛠️How to make changes
Add a new container runtime connector (e.g., Kubernetes, LXC)
- Create a new connector implementation file in connector/ directory (e.g., connector/kubernetes.go) implementing the Connector interface (
connector/kubernetes.go) - Implement the collector in connector/collector/ to handle metric polling from the runtime (e.g., connector/collector/kubernetes.go) (
connector/collector/kubernetes.go) - Implement the manager in connector/manager/ to handle container discovery and lifecycle (e.g., connector/manager/kubernetes.go) (
connector/manager/kubernetes.go) - Register the new connector in connector/main.go's init or factory function (
connector/main.go)
Add a new metric column to the grid display
- Add a new field to the Container struct in container/main.go to store the metric value (
container/main.go) - Update the collector implementations (e.g., connector/collector/docker.go) to populate the new metric during collection (
connector/collector/docker.go) - Add column definition to config/columns.go to make it configurable (
config/columns.go) - Update the compact grid widget (cwidgets/compact/column.go) to render the new column (
cwidgets/compact/column.go)
Add a new interactive keyboard command
- Define the key binding and command handler in keys.go (
keys.go) - Implement the command logic in the appropriate view file (grid.go or cwidgets/single/main.go) (
grid.go) - Optionally add a menu item in menus.go to expose the command in the UI (
menus.go)
Add a new detail view tab in single container mode
- Create a new widget file in cwidgets/single/ (e.g., cwidgets/single/gpu.go) implementing the display logic (
cwidgets/single/gpu.go) - Add the metric data collection to connector/collector/docker.go and container/main.go (
connector/collector/docker.go) - Register the new tab in cwidgets/single/main.go's tab switching logic (
cwidgets/single/main.go)
🔧Why these technologies
- termbox-go / termui — Enables low-level terminal control for real-time TUI rendering without full-screen library overhead
- go-dockerclient — Abstraction over Docker HTTP API for reliable container stats and metadata queries
- opencontainers/runc — Native support for runC containers, complementing Docker to cover OCI container ecosystems
- goprocinfo — Parsing /proc filesystem to extract granular process and cgroup metrics when direct APIs unavailable
- TOML configuration — Human-readable config format for column visibility, keybindings, and runtime customization
⚖️Trade-offs already made
-
Polling-based metric collection vs. streaming subscriptions
- Why: Simpler implementation, works across Docker and runC without subscription overhead
- Consequence: Slight latency in metric freshness (configurable poll interval), but lower resource usage and predictable update cadence
-
Terminal UI over web UI
- Why: Instant launch, low overhead, works over SSH, aligns with Unix philosophy of tools-as-utilities
- Consequence: Limited to terminal-based workflows; no browser history or multi-user dashboard; rendering constrained to terminal dimensions
-
In-memory container state vs. persistent storage
- Why: Faster UI responsiveness, no database overhead, stateless design
- Consequence: Metrics lost on restart; no historical trending; limited to current session data
-
Single connector interface abstraction
- Why: Enables pluggable backends (Docker, runC, mock) without branching logic throughout codebase
- Consequence: Requires separate collector and manager implementations per runtime; more boilerplate for new connectors
🚫Non-goals (don't propose these)
- Persistent metrics storage or historical trending
- Multi-user or networked dashboard sharing
- Real-time alerting or threshold-based notifications
- Support for Windows containers or non-Linux runtimes
- Cluster orchestration (Kubernetes, Swarm) as primary target (connectors planned for future)
🪤Traps & gotchas
Docker socket path defaults to /var/run/docker.sock—will fail silently if socket unavailable or unprivileged. The connector/collector abstraction is tightly coupled to metrics struct fields—adding a new metric requires updates across Container, collector implementations, and cwidgets display code simultaneously. Config file uses different default paths depending on XDG environment (check ~/.config/ctop/config vs ~/.ctop). The compact/ cwidgets assume termbox terminal size; resizing terminal mid-run may cause rendering glitches. Mock implementations in mock.go/collector/mock.go hardcode fake metrics for testing—not realistic for load testing.
🏗️Architecture
💡Concepts to learn
- Container Runtime Interface (CRI) / Runtime Abstraction — ctop uses an adapter pattern (connector/ package) to swap Docker and runC implementations without changing core logic—understanding this pattern is essential to extending support to new runtimes
- Linux cgroups (control groups) — Docker and runC expose CPU, memory, and I/O metrics via cgroups; understanding cgroup hierarchies helps debug why certain metrics appear or are unavailable
- Terminal User Interface (TUI) / Termbox paradigm — ctop renders to termbox (low-level terminal cell buffer); understanding termbox's cell-based rendering model explains layout constraints and why resizing can cause glitches
- Docker API streaming (stats endpoint) — connector/collector/docker.go polls the /containers/{id}/stats endpoint; understanding Docker's streaming stats protocol helps debug metric gaps or delays
- /proc filesystem metrics (Linux procfs) — c9s/goprocinfo dependency reads /proc/stat and /proc/meminfo for host-level context; relevant when debugging why host metrics differ from container-reported metrics
- Config file persistence (TOML format) — ctop saves state via BurntSushi/toml; understanding TOML vs JSON/YAML is relevant when debugging or extending config serialization in config/file.go
🔗Related repos
moby/moby— Docker's main repository; ctop's Docker connector depends on moby/moby's API contracts and container structuresopencontainers/runc— The runC container runtime that ctop integrates with as a primary backend connectornicolaka/netshoot— Complementary container debugging tool; users of ctop often use netshoot for deep packet inspection when ctop shows anomaliesprometheus/prometheus— For users wanting persistent metrics collection from containers ctop monitors; ctop is real-time UI only, no persistencegizak/termui— Direct dependency providing the terminal UI widget primitives that cwidgets/compact/ builds upon
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add unit tests for connector/collector implementations
The connector/collector packages (docker.go, runc.go, proc.go, mock.go) lack visible test files. These are critical paths for metrics collection. Adding tests would improve reliability when adding new container backends and help prevent regressions in metric gathering.
- [ ] Create connector/collector/docker_test.go with tests for DockerCollector.Collect() and error handling
- [ ] Create connector/collector/runc_test.go with tests for RunCCollector against mock runc data
- [ ] Create connector/collector/proc_test.go with tests for proc file parsing (CPU, memory, IO stats)
- [ ] Use mock.go patterns to create fixtures for container metrics
- [ ] Ensure tests cover edge cases like missing cgroups, permission errors, and malformed metric data
Add GitHub Actions CI/CD to replace CircleCI
The repo uses CircleCI (.circleci/config.yml) but GitHub Actions is now standard and would reduce dependency management. This would automate testing across multiple Go versions and build matrix targets (darwin/linux/amd64/arm64).
- [ ] Create .github/workflows/test.yml to run
go test ./...on multiple Go versions (1.16+) - [ ] Create .github/workflows/build.yml to cross-compile binaries for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64
- [ ] Create .github/workflows/lint.yml to run golangci-lint on the codebase
- [ ] Add workflow to build and push Docker image (reference existing Dockerfile) on releases
- [ ] Update README.md with GitHub Actions badges replacing CircleCI badge
Add integration tests for cwidgets/compact and cwidgets/single UI rendering
The UI widget packages (cwidgets/compact and cwidgets/single) have multiple display components (grid.go, row.go, gauge.go, cpu.go, mem.go) but no visible tests. Adding snapshot/golden file tests would catch rendering regressions and ensure cross-platform terminal compatibility.
- [ ] Create cwidgets/compact/grid_test.go with golden file tests comparing rendered grid output against known-good terminal output
- [ ] Create cwidgets/single/main_test.go testing single container view layout with mock container data from connector/collector/mock.go
- [ ] Create test fixtures in cwidgets/testdata/ with expected terminal output for different container states (running, paused, high CPU/memory)
- [ ] Use termbox-go test mode (reference how nsf/termbox-go handles testing) to capture screen buffer output
- [ ] Test responsive resizing behavior for compact grid when terminal width/height change
🌿Good first issues
- Add unit tests for container/sort.go—currently no test coverage visible for sort logic; would improve confidence in column-based sorting behavior
- Document connector interface contract in _docs/connectors.md with a worked example of adding a new runtime (e.g., Kubernetes); current docs lack implementation details needed by contributors
- Add integration tests for config/ package—config/file.go handles TOML parsing but no visible test file (config/file_test.go missing); should test save/load round-trips and XDG path handling
⭐Top contributors
Click to expand
Top contributors
- @bcicen — 45 commits
- @stokito — 40 commits
- [@William Diaz](https://github.com/William Diaz) — 2 commits
- @dependabot[bot] — 2 commits
- @garsue — 2 commits
📝Recent commits
Click to expand
Recent commits
59f00dd— Merge pull request #296 from utix/patch-1 (bcicen)0626540— Merge pull request #306 from jnishwanth/patch-1 (bcicen)a08150e— Merge pull request #303 from wdiazux/master (bcicen)6bf41ea— Add Windows-specific intructions and badge for scoop (#295) (Alex Stelmachonak)3dff997— updated README.md (jnishwanth)458ef62— Required packages to set up the repository (William Diaz)a821deb— Fix apt-key is deprecated (William Diaz)dacd3be— Fix running icon (utix)222a7d7— Squashed commit of the following: (bcicen)9bc26c8— update dockerfile base image -> go1.18 (bcicen)
🔒Security observations
The ctop codebase has a
- High · Outdated Go Docker Client Library —
go.mod - github.com/fsouza/go-dockerclient v1.7.0. The dependency 'github.com/fsouza/go-dockerclient v1.7.0' is significantly outdated (released around 2019). This library may contain known security vulnerabilities and lacks security patches for Docker API interactions. Fix: Update to the latest version of the Docker client library. Consider using the official 'github.com/docker/docker' client or 'github.com/moby/moby' for Docker API interactions. - High · Outdated termui Library with Known Issues —
go.mod - github.com/gizak/termui v2.3.1. The dependency 'github.com/gizak/termui v2.3.1-0.20180817033724-8d4faad06196+incompatible' is pinned to a very old commit from 2018. This UI library may have unpatched security or stability issues. The '+incompatible' tag indicates it doesn't follow semantic versioning properly. Fix: Migrate to a maintained terminal UI library such as 'github.com/charmbracelet/bubbletea' or 'github.com/rivo/tview', or update termui to the latest stable version. - Medium · Outdated Go Version in Docker Build —
Dockerfile - FROM quay.io/vektorcloud/go:1.18. The Dockerfile uses 'quay.io/vektorcloud/go:1.18' which is an old Go version (released in March 2022). This base image likely contains outdated dependencies and security patches. Fix: Update to the latest stable Go version (1.21 or later). Use official golang images when possible: 'FROM golang:1.21-alpine' or similar. - Medium · Missing Security Headers in Multi-stage Build —
Dockerfile - Second stage build. The Dockerfile's final stage uses 'FROM scratch' with no package management, which is good for minimizing attack surface, but there's no verification of binary integrity or signature validation during the build process. Fix: Add signature verification or checksums for the compiled binary. Consider using a minimal base image with security tools if needed for debugging. - Medium · Potential Insecure Docker Socket Access —
connector/docker.go, connector/manager/docker.go. The application interfaces with Docker through the Docker daemon (go-dockerclient). If ctop is run in containers, it likely requires mounting the Docker socket (/var/run/docker.sock), which grants significant privileges. No explicit security context restrictions are documented. Fix: Document security best practices for running ctop. Implement principle of least privilege. Consider using Docker API authentication when possible. Warn users about socket mounting risks in documentation. - Low · Missing Go Module Verification —
go.mod, Dockerfile build steps. The go.mod file lacks go.sum checksums validation in the provided content. While go.sum exists in the file structure, there's no indication of module verification being enforced in the build process. Fix: Ensure 'go mod verify' is executed during CI/CD pipeline. Add explicit go.sum verification step in the Dockerfile or CI configuration. - Low · Deprecated Dependencies Still in Use —
go.mod - indirect dependencies. Several indirect dependencies are marked as deprecated or have stopped receiving updates (e.g., github.com/hako/durafmt marked as indirect, github.com/nu7hatch/gouuid). Fix: Audit all dependencies and replace deprecated packages with maintained alternatives. Remove unused dependencies. - Low · No SBOM or Dependency Security Scanning —
.circleci/config.yml. No evidence of software bill of materials (SBOM) generation or automated dependency vulnerability scanning in CI/CD configuration (based on provided .circleci/config.yml reference). Fix: Integrate tools like 'go-vuln', 'Dependabot', 'Snyk', or 'trivy' into the CI/CD pipeline for automated dependency scanning.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.