v0.6  ·  self-hosted container observability

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.

SQLite, no external DB One binary, one volume MIT licensed Basic auth by default
capscope — logs · shop · live SSE connected
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 
1
binary · zero external services
24h
crash catch-up after downtime
4
alert channels (webhook · Telegram · Slack · Discord)
200k
lines per CSV export
the 3 AM problem

Your container died. Its logs died with it.

Live log viewers show the present tense. Debugging needs the past.

Without CapScope
  • Redeploy erases evidence. docker compose up replaces 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.
With CapScope
  • 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.
features

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.

Also included: secret redaction before storage, Basic auth required by default (the server refuses to start without a password), CSV export up to 200,000 lines, per-app retention and size budgets, INCLUDE/EXCLUDE name filters, and a hot-reloaded JSON alert file.
architecture

One Go process. One SQLite file.

No agent, no sidecar, no collector daemon, no external database.

architecture
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.

quick start

Running in about 30 seconds

Two mounts and one password. Open http://localhost:8080 when it's up.

bash — docker run
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
docker-compose.yml
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:
open source · MIT

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.