Skip to content
BoringStack
GitHub

Error tracking

4 min read

Error tracking

Both apps/api and apps/ui ship Sentry SDKs configured for zero overhead. Swap between Hosted Sentry and self-hosted GlitchTip with a single DSN key.

Sentry API

Protocol

Self-host

Opt-in

1

unified SDK

Both apps/api and apps/ui ship a Sentry SDK. The same SDK talks to:

  • Hosted Sentry; fastest setup, paid past the free tier.
  • Self-hosted GlitchTip; Sentry-API-compatible, runs as an overlay in the infra stack (WITH_GLITCHTIP=1). Zero per-event cost.

The SDKs don’t know which one they’re talking to. Choosing is a one-env-var change.

Sentry SDK on both sides (not a custom shim)

Same SDK interface and error format across both services.

GlitchTip as the self-host option

Wire-compatible with Sentry; runs on the same Postgres + Valkey the app already uses.

Empty DSN: SDK is a no-op

Dev stays clean; tests do not ship error reports.

Replay-on-error on, full-session replays off

Captures the broken flow without storing healthy sessions.

Single env var per side: SENTRY_DSN (API), VITE_SENTRY_DSN (UI)

Same protocol regardless of backend; swap is one redeploy.

flowchart LR
  api["apps/api<br/>@sentry/bun"] -- "events" --> backend{DSN points where?}
  ui["apps/ui<br/>@sentry/react"] -- "events + replays-on-error" --> backend
  backend -- "https://...sentry.io/..." --> sentry["hosted Sentry"]
  backend -- "https://...glitchtip.localhost/..." --> glitchtip["self-hosted GlitchTip"]

Both apps/api (@sentry/bun) and apps/ui (@sentry/react) emit events to the same Sentry-compatible wire protocol. The DSN env var picks the destination: a sentry.io hostname for hosted Sentry, or a glitchtip.localhost hostname for the self-hosted overlay. The SDKs don’t know which one they’re talking to.

API side: Sentry initialises once at boot. If SENTRY_DSN is empty, init is a no-op. The shared captureError helper is wired into unhandled-rejection and uncaught-exception handlers, so anything that escapes the request loop reaches the backend.

UI side: Sentry initialises once at app mount when VITE_SENTRY_DSN is set. Replays-on-error capture the error context; full-session replays are off to avoid capturing video of every session.

GlitchTip is Apache-licensed and Sentry-API-compatible. The infra stack provides it as an overlay:

Terminal window
WITH_GLITCHTIP=1 ./dev.sh up -d

First boot bootstraps a superuser, a default org, and two projects (API and Frontend). Visit http://glitchtip.localhost, grab each project’s DSN, and drop them into the matching env vars.

The overlay reuses the base stack’s Postgres (in a separate glitchtip database) and Valkey (DB 1). Adding GlitchTip costs two extra containers, not a separate database server.

For production hardening (Basic Auth, HTTPS, real SMTP), see the GlitchTip docs under infra/compose.

Change only the DSN:

Sentry (hosted)

API: SENTRY_DSN=https://...@sentry.io/.... UI: VITE_SENTRY_DSN=https://...@sentry.io/....

GlitchTip (self-hosted)

API: SENTRY_DSN=https://...@glitchtip.example.com/.... UI: VITE_SENTRY_DSN=https://...@glitchtip.example.com/....

No SDK changes. The infrastructure is the variable, not the code.