Know when your app breaks, and which bug it is.
Error Keeper is an open-source error tracker for small teams. A one-kilobyte browser snippet and a Node client send crashes to a server you run. It groups the same bug together even when line numbers, ids and bundle hashes differ, alerts you the first time a bug appears, when a bug you fixed comes back, and when one suddenly gets much worse, and shows the latest stack. First, see the grouping on your own stack traces. It runs in your browser, so nothing you paste is uploaded.
1. Group your errors
2. Run the server yourself
git clone https://github.com/Duncan-Haywood/oss-monorepo && cd oss-monorepo/ventures/error-keeper INGEST_KEY=$(openssl rand -hex 16) ADMIN_KEY=$(openssl rand -hex 16) \ ALLOWED_ORIGINS=https://app.example.com node server.mjs
Browser: <script defer src="https://YOUR-HOST/sdk.js" data-key="INGEST_KEY" data-release="1.4.2"></script>. Node: createKeeper({url, key, release}).install(). Open your server's address, enter the admin key and read the issue list. Set ALERT_WEBHOOK_URL for a signed alert that is also a valid Slack or Discord incoming webhook. One-click Render deploy and Docker are in the README.
Questions
Why not use Sentry, Rollbar, Bugsnag or Honeybadger?
They are good products and the right answer for a team that wants source maps, session replay and performance tracing. They also bill by event volume, and the self-hosted Sentry is a dozen containers with Kafka and ClickHouse. Error Keeper does the part most small teams use every day: group the same bug together, tell you when something new or a regression or a spike happens, and show the latest stack. It is one dependency-free Node process with a JSON file, and your stack traces stay on your server.
How does it decide two errors are the same bug?
It builds a key from the error type and the top three in-app stack frames (function and file name, never line numbers or bundle hashes), so a deploy that shifts lines does not split a group. With no stack it uses the message with numbers, ids, quoted values and URLs removed. You can override the key with your own fingerprint. The box above runs the real grouping code on whatever you paste.
What counts as a regression or a spike?
Mark an issue resolved in a release. If an event arrives from that release or a later one, it reopens and alerts as a regression. A spike is at least 10 events in the current hour and well above that issue's own 24-hour baseline (mean plus four standard deviations, and at least three times the mean), so a noisy bug does not page you every hour.
What does it not do?
No source-map unminifying, no session replay, no performance tracing, no multi-user accounts or per-user permissions, and the JSON file is fine for thousands of events a day, not millions. Browser errors from minified bundles show minified frames; ship unminified stacks to your own server or use a service that unminifies if you need that.
Is it safe to send real errors to it?
It masks emails, bearer tokens, common API-key shapes and sensitive query values before storing anything, and keeps user ids only as hashes. That is a safety net, not a guarantee: you run the server, so you are responsible for what your app sends, for securing the keys, for backups and for the privacy rules where you and your users live.
Part of the self-hosted SaaS stack: 27 open-source tools you run yourself.