Containers never truly
disappear.
CapScope 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 gets OOM killed. Redeploy all you want — the evidence stays searchable.
03:14:07.118 INFO shop.2 order accepted id=8841 total=42.50
03:14:07.402 WARN shop.2 upstream payment-gateway latency=1930ms
03:14:08.910 ERROR shop.2 pool: database is locked (timeout after 5s)
03:14:09.066 ERROR shop.2 retry 3/3 failed: dial tcp 10.0.0.7:5432
03:14:09.301 FATAL shop.2 exiting: cannot open database
03:14:09.318 EVENT die exit=137 oom=true restarts_window=4
03:14:09.340 SAVED snapshot #184 · summary written · alert dispatched
Your container died. Its logs died with it.
Live log viewers show the present tense. Debugging needs the past.
- Redeploy erases evidence.
docker compose upreplaces the container and takes stdout, exit codes and memory trends with it. - The observability tax. Prometheus + Grafana + Loki + exporters is a lot of moving parts for one server or a homelab.
- Crashes arrive as a shrug. "Restarting…" with no exit code, no last lines, no idea whether it was your release or the OOM killer.
- Secrets in plain logs. JWTs and passwords get copied into screenshots and pastebins.
- History that survives redeploy. Logs and metrics land in SQLite with age and size retention — search an app's logs long after its container is gone.
- One container, one volume, one password. No external database, no scraping stack, no build step for the UI.
- Crash snapshots. Exit code, OOM flag, restart count, uptime, last log lines, metric trend and a plain-language summary — exported as Markdown for incident handoff.
- Redaction on ingest. JWTs, bearer tokens,
password=, URL credentials, AWS keys and PEM keys are masked before anything touches disk.
Everything you need to answer "what happened?"
Built for self-hosters, homelab brewers, cloud & DevOps engineers and platform teams who run Docker on one box.
Live tail over SSE
Per-app sidebar, virtual-scrolled live tail, pause & follow, load older, and a split view for up to 4 replicas side by side. Click any JSON line to pretty-print it.
Search that finds it
FTS5 full-text index with prefix matching, regex mode, raw SQL over logs, metrics, containers and snapshots, plus filters for level, stream and replica.
Persistent history
Everything is stored in SQLite with age and size retention, per-app budgets and a hard MAX_DB_MB cap. The oldest data is dropped first, always.
Crash snapshots
Captured on non-zero exit or OOM kill: exit code, restart count, uptime, ring-buffered last lines, metric samples, rule-based summary and Markdown export.
Restart-loop detection
Deaths are counted in a sliding window; looping containers get a ↻ badge in the UI and a called-out summary — rollouts stay out of the count.
CPU & memory charts
Bucketed samples from 15 minutes to 7 days, summed per app across replicas, with net, block and PID counters alongside.
Access-log dashboard
GoAccess-style panels from your reverse proxy: status codes, top paths, client IPs (anonymized by default), referrers, hits/errors timeline and CSV export.
Deploy markers
Record releases from CI or the UI. Markers appear on the CPU/memory charts, inline in the log timeline, and on the crash detail as "Last deploy: v1.4".
Alerts
Dead-man, restart loop, metric threshold with duration, and log-pattern rules. Webhook, Telegram, Slack and Discord channels with a test button.
INCLUDE/EXCLUDE name filters, and a hot-reloaded JSON alert file.One Go process. One SQLite file.
No agent, no sidecar, no collector daemon, no external database.
Docker Engine → CapScope (one Go binary) → SQLite (one file)
│
├─ collector: follows logs + stats per container
├─ hub: SSE fan-out to the browser
├─ store: WAL mode, batched inserts, retention
└─ api: REST + SSE + embedded single-page UI
Collector
Follows Docker's log and stats streams, redacts, level-tags and batches rows. Ring buffers keep the final lines for snapshots.
Store
WAL mode, synchronous=NORMAL, single writer plus a read-only pool — UI queries never block ingestion.
Hub
Non-blocking fan-out of committed rows to SSE subscribers, with per-subscriber filters and a drop counter.
API
Plain REST + SSE + the embedded UI. The dashboard only ever calls the same public API you do.
Deep dive in the architecture guide — failure modes, concurrency model, storage decisions and trade-offs.
Running in about 30 seconds
Two mounts and one password. Open http://localhost:8080 when it's up.
docker run -d --name capscope \
-p 8080:8080 \
-e AUTH_USER=admin \
-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
services:
capscope:
image: ghcr.io/hacrex/capscope:latest
ports:
- "8080:8080"
environment:
AUTH_USER: admin
AUTH_PASS: change-me
MAX_DB_MB: 2048
EXCLUDE: "^captain-"
volumes:
- capscope-data:/data
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
volumes:
capscope-data:
Runs wherever Docker runs
Every guide covers storage, socket access, TLS, health checks and single-replica rules.
Stop losing the logs that matter.
Clone the repo, run make demo for a UI with fake data, or deploy it to your server in one command.