Bugsink 2.0.12

Klaas van Schelven
Klaas van Schelven; February 10 - 2 min read
Bugsink 2.0.12

Since Bugsink 2.0 shipped in September 2025, there hadn’t been a proper blog update.

That wasn’t because nothing was happening… quite the opposite. A steady stream of small releases landed in that period, mostly fixes, hardening, and practical improvements based on user feedback. It just took until now to sit down and write about it.

Quota and retention

Since 2.0, most quota work has been about making retention and rate limits behave more predictably in practice.

File deletion during eviction was moved to run on (DB) commit, with a small follow-up fix to ensure the cleanup actually executes. Also introduced: a simple command, delete_by_age_until_under_retention_max, which deletes the oldest events in batches until a project is back under its retention cap.

A handful of smaller fixes tightened how limits are applied: site-wide retention always wins over per-project values, invalid “max event count” settings are rejected, and the site-wide monthly ingestion limit is consistently enforced.

Separately, quota checks were changed to rely on project_digest_order instead of raw row counts in a time window, because counting rows breaks down once retention starts evicting old events. An optional command (fix_project_digest_order) can backfill this for existing data.

Storage

File-based event storage gained optional compression. Bugsink can now write events as plain JSON (the default), or as gzip (.json.gz) or brotli (.json.br). The choice is made per storage backend when it is configured, with a tunable compression level (defaulting to gzip level 9 or brotli quality 11).

The storage layer was also made more flexible: instead of a fixed basepath, you can now provide a callable (get_basepath) to compute the storage directory dynamically. Unexpected configuration keys are ignored (with a warning) so new options won’t break existing setups.

Internally, reads and writes now go through a small streaming wrapper so compressed files behave like normal UTF-8 text files to the rest of the system. The list() method was hardened to return real UUIDs and to handle uninitialized storage directories gracefully instead of crashing.

Alerts

The alerting backends were extended and polished rather than redesigned. Mattermost became a first-class option alongside Slack, with support for sending alerts to specific channels or direct messages via an optional channel field in the webhook configuration. Discord was also added as a backend, with the explicit caveat that Discord webhooks don’t support per-message channel overrides.

Slack alerts were cleaned up so the issue title appears in the message header, with a clearer “view on Bugsink” link instead of embedding the title inside the URL itself. Minor formatting changes make messages more consistent between Slack and Mattermost while keeping the same underlying payload structure.

In summary

Between September 2025 and 2.0.12 there was no single big feature release; just a run of small, practical improvements driven by real usage.

Most of the work went into making the system sturdier rather than shinier: quotas became more reliable under eviction, retention got safer and easier to recover from, file storage gained optional gzip/brotli compression, and alerting options broadened to include Mattermost and Discord with cleaner Slack messages.

Nothing here changes what Bugsink fundamentally is. It just works a bit better, fails more predictably, and is easier to run in messy real-world setups.