Skip to content
edde746Public

About

Lightweight, self-hosted error tracking compatible with Sentry SDKs

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Bugs

A lightweight, self-hosted error tracking system compatible with Sentry SDKs.

Bugs receives, processes, and visualizes application errors with source map support, release management, and alerting — all backed by SQLite in a single binary.

Comparison

Resource usage of Docker images, measured idle after startup.

Bugs Rustrak Bugsink GlitchTip
Language Rust Rust + Node.js Python Python
Database SQLite (embedded) SQLite or PostgreSQL SQLite or PostgreSQL PostgreSQL (required)
Docker image size 40 MB 443 MB 649 MB 1.05 GB
Idle RAM ~3 MB ~68 MB ~285 MB ~145 MB
Total RAM (with deps) ~3 MB ~100 MB ~285 MB ~190 MB
Containers needed 1 3 1 2+

Features

Bugs Rustrak Bugsink GlitchTip
Sentry SDK compatible ✓ ✓ ✓ ✓
Source maps ✓ ✗ ✓ ✓
Releases & deploys ✓ ✗ ✓ ✓
Performance monitoring ✓ ✗ ✗ ✓
Alerts ✓ ✓ ✓ ✓
User feedback ✓ ✗ ✓ ✓
Full-text search ✓ ✗ ✓ ✓
Environments ✓ ✓ ✓ ✓
Multi-user / teams ✗ ✗ ✓ ✓
Uptime monitoring ✗ ✗ ✗ ✓
Retention policies ✓ ✗ ✓ ✓

Quick start

Docker

# Using the pre-built image from GitHub Container Registry
docker run -p 9000:9000 -v bugs-data:/data ghcr.io/edde746/bugs:latest

# Or build from source
docker build -t bugs .
docker run -p 9000:9000 -v bugs-data:/data bugs

From source

# Build frontend
cd frontend && bun install --frozen-lockfile && bun run build && cd ..

# Build and run
cargo build --release
./target/release/bugs

The UI is available at http://localhost:9000.

Configuration

Configuration is loaded from (highest precedence first):

  1. Environment variables prefixed with BUGS_ (nested keys split by __)
  2. A bugs.toml file in the working directory
  3. Built-in defaults

Options

Variable TOML key Default Description
BUGS_BIND_ADDRESS bind_address 0.0.0.0:9000 Listen address
BUGS_DATABASE_PATH database_path ./data/bugs.db SQLite database path
BUGS_ARTIFACTS_DIR artifacts_dir ./data/artifacts Release file storage
BUGS_RETENTION_DAYS retention_days 90 Days to keep events
BUGS_ENVELOPE_RETENTION_HOURS envelope_retention_hours 24 Hours to keep raw envelopes
BUGS_WORKER_THREADS worker_threads 4 Background processing threads
BUGS_AUTH__ADMIN_TOKEN auth.admin_token (empty) Bearer token for management API
BUGS_MCP__ENABLED mcp.enabled false Expose MCP server at /mcp for LLM agents

SQLite tuning

Variable Default Description
BUGS_SQLITE__SYNCHRONOUS NORMAL SQLite synchronous mode
BUGS_SQLITE__CACHE_SIZE_MB 64 Page cache size
BUGS_SQLITE__READER_CONNECTIONS 8 Read connection pool size
BUGS_SQLITE__MMAP_SIZE_MB 256 Memory-mapped I/O size
BUGS_SQLITE__CHECKPOINT_INTERVAL_BATCHES 10 WAL checkpoint frequency

Ingest limits

Variable Default Description
BUGS_INGEST__MAX_RAW_REQUEST_BYTES 20 MB Max raw request body
BUGS_INGEST__MAX_ENVELOPE_BYTES 10 MB Max decompressed envelope
BUGS_INGEST__MAX_EVENT_ITEM_BYTES 1 MB Max single event item
BUGS_INGEST__MAX_ITEMS_PER_ENVELOPE 100 Max items per envelope

Admin upload limits

Variable Default Description
BUGS_UPLOADS__MAX_BYTES 2 GB Max size for admin-authenticated uploads (dSYMs, release artifacts)
BUGS_UPLOADS__CHUNK_RETENTION_HOURS 12 Hours to keep chunk-upload blobs after last use; active uploads refresh this TTL
BUGS_UPLOADS__ASSEMBLY_CONCURRENCY 1 Maximum simultaneous DIF conversions per process; independent of event workers
BUGS_UPLOADS__MAX_WAIT_SECS 600 Advertised client polling budget for DIF processing; not an HTTP connection timeout

Native and Dart symbol uploads

The DIF assemble endpoint returns not_found with missing chunk hashes, assembling while queued or processing, and ok or error on completion. Clients must wait for processing (sentry-cli debug-files upload --wait-for 600) to detect conversion failures. Expensive hashing and SymCache conversion do not run in the HTTP handler.

Assembly jobs and completed receipts are stored in SQLite. Admitted jobs pin their chunks with hard links under the artifacts directory, so chunk retention cannot remove active inputs. Workers renew 60-second leases; unfinished jobs are reclaimed after a crashed worker's lease expires. A job claimed a fourth time fails with an error instead of being converted again, so a file that kills the process (e.g. out of memory) cannot crash-loop it. Completed receipts survive chunk expiry and check the current canonical artifact before returning success. Receipts written by older processing (before CFI extraction) are discarded, so the next upload of the same file is processed again. Failed attempts stay visible for 60 seconds before a new request may retry them.

Native artifacts are selected by project, debug ID, architecture and kind. Immutable content-addressed files prevent concurrent replacement of mapped caches; measured debug/line information takes precedence over stripped symbols. Keep assembly concurrency at one on memory-constrained deployments: chunk-upload concurrency does not limit conversion memory.

The chunk-upload capabilities include pdbs; without it sentry-cli silently skips every PE and PDB file. A PDB whose age sentry-cli corrected to match its PE is stored under the corrected debug ID; only the age may differ from the file's own. Converting a PDB copies nearly all of it into memory (type, ID and module streams), on top of the SymCache being built, so budget well over the size of the largest PDB per assembly worker. Clients wait at most the advertised maxWait from their first assemble poll, including time queued behind other uploads; a file still processing then is reported as an error by sentry-cli but finishes on the server.

Frames are symbolicated like Sentry: addr_mode: "rel:<debug_id or index>" addresses are image-relative, inlined calls expand into one frame each (outermost first), and caller frames are looked up at their call instruction unless the stacktrace's instruction_addr_adjustment says the client already adjusted them. A function name sent by the client is replaced only by real debug info (line tables, inlinees), never by a bare symbol-table guess. A symbol-table lookup that lands in an unsized last symbol (such as ELF _fini, which is followed only by data) yields no name.

Objects with unwind information (ELF .eh_frame/.debug_frame, Mach-O compact unwind, PE .pdata, x86 PDB frame data, Breakpad STACK records) also get a CFI cache next to their SymCache. Crashpad minidumps are stackwalked with the CFI of their modules' uploaded debug files and fall back to frame pointers and stack scanning elsewhere; each frame's trust records the method. objcopy --only-keep-debug files keep .eh_frame only as an empty placeholder, so upload the shipped (stripped) binaries as well.

Before upgrading an existing deployment, back up both the database and artifacts. Migrations 019–020 rebuild artifact identity and add durable assembly state; schema changes and migration markers commit atomically. Rolling back to an older binary requires the pre-upgrade backup, not just changing the image tag.

Authentication

Admin API

Set BUGS_AUTH__ADMIN_TOKEN to secure the management API and web UI. Requests must include Authorization: Bearer <token>. The web UI will show a login page when auth is enabled. If no token is configured, the management API is open — a warning is logged at startup.

Ingest

Ingest endpoints authenticate via Sentry DSN keys. Create a project in the UI to get a DSN, then configure your Sentry SDK with it.

MCP server

Set BUGS_MCP__ENABLED=true to expose an MCP server (Streamable HTTP) at /mcp, letting LLM agents query and triage your instance: list projects and issues, read full event payloads (stack traces, breadcrumbs, variables), full-text search, resolve/ignore issues, and leave comments. Destructive operations (deletes, project/key management) are not exposed.

The endpoint shares the management API's bearer-token auth. Connect from Claude Code:

claude mcp add --transport http bugs http://localhost:9000/mcp \
  --header "Authorization: Bearer <admin_token>"

Sentry SDK integration

Point any Sentry SDK at your Bugs instance:

Sentry.init({
  dsn: "http://<public_key>@<bugs-host>:9000/<project_id>",
});

The DSN is shown in project settings after creating a project.

Database migrations

Schema changes live in migrations/ as numbered SQL files. They are applied once in order on startup and tracked in the _migrations table.

The migration runner is forward-only — there is no rollback mechanism and no down migrations. To change an existing schema object, add a new migration that performs the alteration (SQLite often requires the full table-rebuild pattern: new table, INSERT SELECT, drop, rename; see 014_event_tags_project_fk.sql). Don't edit files that have already been released.

Development

# Backend (auto-reloads with cargo-watch)
cargo watch -x run

# Frontend dev server (proxies /api to localhost:9000)
cd frontend && bun run dev

Production Docker Compose

services:
  bugs:
    image: ghcr.io/edde746/bugs:latest
    restart: unless-stopped
    ports:
      - "9000:9000"
    volumes:
      - bugs-data:/data
    environment:
      BUGS_AUTH__ADMIN_TOKEN: "change-me-to-a-secret-token"
      BUGS_RETENTION_DAYS: "90"

volumes:
  bugs-data:

About

Lightweight, self-hosted error tracking compatible with Sentry SDKs

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages