CapScope documentation

CapScope is a single Go binary that watches your Docker daemon, stores logs and CPU/memory samples in SQLite, and keeps a crash snapshot every time a container exits non-zero or is OOM killed. This section covers concepts, configuration, alerting, internals and the HTTP API.

What it does

AreaWhat you get
Live logsPer-app sidebar, virtual-scrolled live tail over SSE, pause/follow, load older, split view for up to 4 replicas side by side, JSON pretty-print on click.
Search historyFTS5 full-text search, regex mode, or raw SQL via /api/sql; filter by level, stream and replica.
History after redeployLogs and samples persist in SQLite with age and size retention — searchable long after the container is gone.
Crash snapshotsExit code, OOM flag, restart count, uptime, last N log lines, last N metric samples, plain-language summary, Markdown export.
Restart loopsDeaths counted in a sliding window; looping containers flagged with ↻.
Access logsGoAccess-style panels: status codes, top paths, IPs (anonymized), referrers, timeline, CSV export.
Deploy markersRecorded from CI or the UI; drawn on charts, inline in the timeline, attached to crash details.
AlertsFour rule kinds, four channels, hot-reloaded rules file, test button in the UI.
SecuritySecret redaction before storage, Basic auth required by default.

Core concepts

Apps and replicas

CapScope groups containers into apps. CapRover's Swarm service label com.docker.swarm.service.name wins first, then Docker Compose labels, then the container name. Replica slots (srv-captain--shop.1.x) are split off so replicas of one app appear together in the sidebar and can be compared side by side.

Where data lives

One SQLite file, normally /data/capscope.db on a persistent volume. Tables: logs, metrics, containers, snapshots, access, alerts, deploys and state. WAL mode plus a read-only connection pool means dashboard queries never block ingestion.

Retention runs 30 seconds after start, then every 10 minutes: age-based deletion in batches of 5000 rows, then per-app budgets, then the MAX_DB_MB cap (oldest logs first), then incremental_vacuum.
  • Text — case-insensitive FTS5 token/prefix index, falling back to LIKE so mid-word searches still work.
  • Regex — case-insensitive regular expression; invalid patterns return 400.
  • SQL — one read-only SELECT over the stored tables with a 5-second deadline and a 5000-row cap.

Lifecycle of a crash

Event arrives

Docker emits die / oom / kill. The collector detaches the container's tracker so a fast restart cannot race it.

Evidence is flushed

The log follower is given up to 2 seconds to drain. Snapshot evidence comes from in-memory ring buffers, not the database, because writes are batched and can lag by up to 250 ms.

Classification

OOM wins, exit 0 is clean_exit, an earlier stop/kill signal makes it killed, everything else is crash.

Snapshot + summary

Metadata, trimmed inspect, last 200 lines and 60 samples are stored with a rule-based summary. Deaths are deduplicated by (container_id, die_ts).

Handoff

Export as Markdown — exit code, restart window, last deploy, trend and log tail — straight into an incident channel.

Requirements

NeedDetail
docker.sockRead access to the Docker Engine API — the socket itself, or a read-only socket proxy via DOCKER_HOST=tcp://….
/data volumeSQLite lives here. Without it, history and snapshots die on redeploy.
AUTH_PASSMandatory. The server refuses to start without it unless ALLOW_NO_AUTH=true.
Port8080 in the container. Terminate TLS in front of it — Basic auth over plain HTTP travels in clear text.
Log driversjson-file, local and journald are supported through the Docker logs API.
ReplicasExactly one. Two CapScope instances against the same database would fight over the single writer.
Docker socket access is root equivalent. Use Basic auth, HTTPS, and preferably a read-only socket proxy in production. Redaction and level detection are heuristics — never treat redaction as your only secret protection.

Install it

bash
docker run -d --name capscope \
  -p 8080:8080 \
  -e AUTH_PASS='change-me' \
  -v capscope-data:/data \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --restart unless-stopped \
  ghcr.io/hacrex/capscope:latest

Or build from source:

bash
git clone https://github.com/hacrex/CapScope.git
cd CapScope
make run      # http://127.0.0.1:8080 (local testing, no auth)
make demo     # UI with fake data
make test     # full test suite
Why "CapScope"? The name is Captain + Scope - a captain who checks his containers. A captain's job is to know what is running, what is failing and what went over the side, and that is exactly what this tool does for a Docker host. It also nods at CapRover, whose Swarm tasks are prefixed captain-.

Next: platform-specific deployment steps in the deployment guides, or dive into the configuration reference.