Error tracking is one of those things that feels solved. Sentry exists. Rollbar exists. Datadog will happily charge you $400/month to tell you your server is on fire. So why build another one?
Because most teams don’t need a SaaS platform. They need a binary that runs in Docker, speaks the Sentry protocol, and doesn’t phone home.
The Problem with the Alternatives
Sentry is excellent. Their open-source self-hosted version is also excellent — and it requires PostgreSQL, Redis, Kafka, Zookeeper, Celery, and a minimum of 8GB RAM to run locally. That’s fine for a company. It’s overkill for a startup, a side project, or a team that just wants to know when their app crashes.
The lightweight alternatives either abandoned the Sentry SDK ecosystem (forcing you to rewrite your instrumentation) or they’re still cloud-only.
What Rustrak Does
Rustrak is an API server written in Rust that accepts events from any Sentry SDK and stores them in PostgreSQL. That’s it.
┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Your App │────▶│ Rustrak Server │────▶│ PostgreSQL │
│ (Sentry SDK) │ │ (Rust/Actix) │ │ │
└─────────────────┘ └─────────────────┘ └──────────────┘You point your existing SENTRY_DSN at a Rustrak instance and it works. No SDK changes. No vendor lock-in. Your existing error tracking code stays exactly as-is.
Architecture: Two-Phase Ingestion
One design decision we’re particularly happy with is the two-phase ingestion model:
Phase 1 — Ingest (synchronous, <50ms): The SDK sends an event envelope. Rustrak parses it, validates the DSN and auth token, stores the raw event, and returns 200 OK. This phase is intentionally minimal — we prioritize acknowledging the event over processing it.
Phase 2 — Digest (asynchronous): A background worker picks up the raw event, calculates its fingerprint, finds or creates the corresponding issue, and updates all the counters. This happens out of band and doesn’t affect the SDK’s experience.
This matters because ingestion latency directly affects your application. A slow error tracker can cascade into production issues. By keeping Phase 1 under 50ms and doing all the heavy work asynchronously, the SDK always gets a fast response.
Issue Grouping
Events get grouped into Issues using a deterministic fingerprinting algorithm:
- If the event has a custom
fingerprintfield, use that. - Otherwise, hash
exception_type + first_line_of_message + transaction. - For log events, hash
log_message + transaction. - Fallback to the event type.
The same error happening 1,000 times shows up as one Issue with a count of 1,000. This is the core value of error tracking — noise reduction.
Memory Footprint
One of our goals was a server that runs comfortably in constrained environments. At idle, Rustrak sits at around 30–50MB of RAM. Under load (hundreds of events/second), it stays well under 100MB. The async runtime (Tokio) and the lightweight HTTP framework (Actix-web) make this possible without any manual tuning.
For comparison: the minimum recommended RAM for Sentry’s self-hosted stack is 3GB.
Getting Started
The fastest way is with Docker — no database setup required, SQLite is included by default.
# docker-compose.yml
services:
server:
image: abians7/rustrak-server:latest
ports:
- "8080:8080"
volumes:
- rustrak_data:/data
environment:
- SESSION_SECRET_KEY=${SESSION_SECRET_KEY}
- CREATE_SUPERUSER=${CREATE_SUPERUSER}
restart: unless-stopped
ui:
image: abians7/rustrak-ui:latest
ports:
- "3000:3000"
environment:
- RUSTRAK_API_URL=http://server:8080
depends_on:
- server
restart: unless-stopped
volumes:
rustrak_data:SESSION_SECRET_KEY=$(openssl rand -hex 32)
CREATE_SUPERUSER=admin@example.com:changeme123
docker compose up -dOpen http://localhost:3000, log in, create a project, and point your existing Sentry SDK at it:
Sentry.init({ dsn: "http://<key>@localhost:8080/<project_id>" })No SDK changes. It just works. The Getting Started guide covers PostgreSQL, environment variables, and production setup in detail.