# Error Tracking Without Sentry: Self-Hosted Setup

> Set up error tracking without Sentry: point your existing Sentry SDK at a self-hosted DSN or build a basic tracker. Grouping, source maps and alerts covered.

David Viejo · Published 2026-03-12 · Updated 2026-10-10 · 12 min read

Canonical: https://temps.sh/blog/how-to-set-up-error-tracking-without-sentry

To set up error tracking without Sentry, keep the Sentry SDK and point its DSN at a self-hosted, Sentry-compatible endpoint — Temps (which injects SENTRY_DSN into apps it deploys) or GlitchTip — or build a minimal tracker with window.onerror, a fingerprint and a PostgreSQL table.

**To set up error tracking without Sentry, point the Sentry SDK you already use at a self-hosted, Sentry-compatible endpoint, or build a small tracker yourself.** There are three practical paths: use [Temps](https://temps.sh/error-tracking), a self-hosted deployment platform with a Sentry-compatible DSN built in; self-host GlitchTip, a standalone Sentry-SDK-compatible tracker that runs on 512MB of RAM; or write a minimal tracker in about 80 lines of JavaScript and a PostgreSQL table. This guide covers how error tracking works, then walks through each path.

> **TL;DR:** Sentry's free tier gives you 5,000 errors per month for one user — a single bug loop can exhaust it in minutes. Temps bundles Sentry-compatible error tracking into the same platform that handles deployment, analytics, session replay, and uptime monitoring — free to self-host (dual MIT / Apache 2.0), with no per-seat or per-event fees. If you'd rather run a standalone tracker, GlitchTip is Sentry-SDK-compatible and runs on [512MB of RAM](https://glitchtip.com/documentation/install), or you can build basic error tracking in \~80 lines of code.

---

## Sentry vs. Self-Hosted Alternatives: Quick Comparison

|                       | **Temps**                                                                | **GlitchTip**    | **Sentry SaaS**                              |
| --------------------- | ------------------------------------------------------------------------ | ---------------- | -------------------------------------------- |
| **Cost**              | Free (self-host); Temps Cloud from $29/mo (coming soon)                  | Free (self-host) | $26/mo (Team)                                |
| **Pricing model**     | Flat / no per-event fees                                                 | None             | Per-event overages                           |
| **Min RAM**           | Part of deployment platform                                              | 512 MB           | 16 GB + 16 GB swap (self-hosted)             |
| **Source maps**       | Uploaded by the Sentry build plugin; credentials injected on git deploys | Manual upload    | Uploaded by the Sentry build plugin          |
| **Sentry SDK compat** | Sentry-compatible DSN                                                    | Yes              | Native                                       |
| **Linked to deploys** | Yes — built in                                                           | No               | Via releases and deploy tracking             |
| **Session replay**    | Yes                                                                      | No               | 50 replays/mo included, pay-as-you-go beyond |
| **License**           | MIT / Apache 2.0                                                         | MIT              | FSL (converts to Apache-2.0 after 2 years)   |

---

## What Does Good Error Tracking Actually Need?

Production error tracking needs five core capabilities. The gap between "we log errors" and "we track errors" is enormous.

### Stack traces with source maps

A raw stack trace from minified JavaScript is useless. You'll see something like `a.js:1:4523` — good luck finding that bug. Source map support transforms that into `checkout.tsx:47:handleSubmit`, pointing you to the exact line in your original source code.

Server-side errors need the same treatment. Node.js stack traces are readable by default, but transpiled TypeScript or bundled code needs source map resolution. Without it, you're reading compiled output.

### Error grouping and deduplication

A single bug can generate thousands of identical errors. If your checkout form throws a TypeError on every submission, you don't want 10,000 separate alerts. You want one group that says "TypeError in handleSubmit — 10,000 occurrences, 847 affected users."

Grouping works by fingerprinting errors — combining the error type, message, and stack trace into a hash. Similar errors collapse into a single issue. This is the feature that separates real error tracking from a log file full of noise.

### Release tracking

Knowing _when_ a bug was introduced changes everything. Release tracking tags every error with the deploy that was running when it occurred. "This error started appearing after deploy `v2.4.1` at 3:15 PM" is actionable. "This error exists somewhere" is not.

### User context and breadcrumbs

Who hit this bug? What were they doing before it happened? User context attaches identifying information — user ID, email, account plan — to each error. Breadcrumbs record the trail of events leading up to the crash: page navigations, button clicks, API calls, console logs.

Together, they let you reproduce the exact sequence that triggers a bug without asking the user "what did you click?"

### Alerting that isn't noise

Good alerting triggers on _new_ errors and _spikes_ in existing ones. If a known bug fires 50 times a day and that's normal, don't page anyone. But if a new TypeError appears after a deploy, or an existing error jumps from 50 to 5,000 occurrences, that's worth a Slack notification or a PagerDuty alert.

---

## How Does Error Tracking Actually Work?

Browser-side error capture relies on two global event handlers, [`error`](https://developer.mozilla.org/en-US/docs/Web/API/Window/error_event) and `unhandledrejection`, which together catch uncaught exceptions and rejected promises. Understanding the mechanics helps you evaluate any tool — or build your own.

### Client-side: window\.onerror and unhandledrejection

Every error tracking SDK starts with these two handlers:

```javascript
// Catches synchronous errors and runtime exceptions
window.onerror = function(message, source, lineno, colno, error) {
  sendToTracker({ message, source, lineno, colno, stack: error?.stack });
};

// Catches unhandled Promise rejections
window.addEventListener('unhandledrejection', function(event) {
  sendToTracker({ message: event.reason?.message, stack: event.reason?.stack });
});
```

That's the foundation. React apps add Error Boundaries to catch component-level crashes. Vue has `app.config.errorHandler`. Svelte has `handleError`. But they all funnel back to a function that serializes the error and sends it to your backend.

### Server-side: middleware and process handlers

On the server, the pattern depends on your framework. Express uses error middleware:

```javascript
app.use((err, req, res, next) => {
  sendToTracker({
    message: err.message,
    stack: err.stack,
    url: req.originalUrl,
    method: req.method,
    userId: req.user?.id
  });
  res.status(500).json({ error: 'Internal server error' });
});
```

Node.js also needs `process.on('uncaughtException')` and `process.on('unhandledRejection')` as safety nets for errors that escape your middleware chain.

### Source map upload and resolution

Here's how source map resolution works in practice. During your build step, your bundler (Webpack, Vite, esbuild) generates `.map` files alongside your minified output. You upload those maps to your error tracking service, tagged with the release version.

When an error arrives with a minified stack trace, the service looks up the corresponding source map, runs the mapping algorithm, and displays the original file name, line number, and column. Without this step, frontend error tracking is nearly useless.

### Error fingerprinting and sampling

Not every error deserves storage. Fingerprinting hashes the error type, message (with variable parts stripped), and the top frames of the stack trace. Identical fingerprints collapse into one group. But what about high-volume errors that would overwhelm your storage?

Sampling helps. You can capture 100% of _unique_ errors while sampling high-frequency repeats. A common approach: always capture the first occurrence, then sample at 10% for errors that fire more than 100 times per hour.

When error tracking is built into the deployment platform, the release and upload credentials can come from the deploy itself, so source maps line up with the exact build that produced the error.

---

## Why Is SaaS Error Tracking So Expensive?

According to [Sentry's pricing page](https://sentry.io/pricing/) (checked October 2026), the Team plan is $26/month billed annually for 50,000 errors with unlimited users. Other vendors price by events, hosts or sessions, and the costs compound fast for growing applications.

Here's what the major players charge in 2026:

| Tool                  | Free Tier       | Paid Starting At               | Event/Error Limit                             | Per-Event Overage                                                   |
| --------------------- | --------------- | ------------------------------ | --------------------------------------------- | ------------------------------------------------------------------- |
| Sentry                | 5K errors/mo    | $26/mo (Team, billed annually) | 50K errors                                    | $0.00036/error pay-as-you-go for the next 50K ($0.00029 if prepaid) |
| Bugsnag (Insight Hub) | 7,500 events/mo | $20/mo billed yearly (Select)  | 50K events (account-wide, unlimited projects) | Higher volume tiers                                                 |
| Rollbar               | 5K events/mo    | $9/mo (Essentials)             | 10K occurrences                               | Higher volume tiers                                                 |

on each vendor's pricing page

### The per-event trap

Per-event pricing sounds fair until you ship a bug that loops. A single uncaught exception in a `useEffect` that runs on every render can generate hundreds of errors per second. At Sentry's Team tier, that's your entire 50K monthly quota burned in under an hour.

You end up choosing between two bad options: set aggressive client-side rate limiting (and risk missing real errors) or pay overage fees that can double or triple your bill. Neither is great.

### Costs compound across services

Most teams don't run a single service. You've got a frontend, an API server, maybe a background job processor and a webhook handler. Each one generates errors independently. Sentry counts them all against one pool, which is better than per-project pricing — but 50K events across four services means roughly 12,500 per service. That's not a lot.

Even healthy applications can generate a steady stream of errors during normal operation — not from bugs, but from network timeouts, third-party API failures, and browser extensions injecting broken scripts. SaaS error tracking treats all of these as billable events.

### What are you actually paying for?

The expensive part of SaaS error tracking isn't the technology. It's the storage and the infrastructure to ingest, process, and query millions of events in real time. Self-hosted tools prove this — the actual error tracking logic fits in a surprisingly small codebase. You're paying for someone else to run the database.

Could you run that database yourself? Absolutely. That's exactly what open-source alternatives do.

---

## What Open-Source Error Tracking Alternatives Exist?

According to the [GlitchTip documentation](https://glitchtip.com/documentation/install), it recommends 512MB of RAM (256MB minimum for the all-in-one setup) and provides Sentry-compatible error tracking for free. Self-hosted Sentry, by contrast, documents a minimum of 16GB of RAM plus 16GB of swap, and its compose file defines 50+ services. The resource gap between these tools is massive.

### Self-hosted Sentry

Sentry publishes a self-hosted option that's feature-complete with their SaaS product. It's genuinely powerful. It's also genuinely heavy.

The `self-hosted` repository on GitHub defines 50+ compose services: PostgreSQL, ClickHouse, Kafka, Redis, Snuba consumers, Symbolicator, Relay, and many Sentry worker processes. The [documented minimum](https://develop.sentry.dev/self-hosted/) is 4 CPU cores and 16GB of RAM plus 16GB of swap, with 32GB recommended.

It works. But running mini-Sentry on your infrastructure feels like deploying a small data center just to catch JavaScript errors.

### GlitchTip

GlitchTip takes the opposite approach. It's a lightweight, Sentry-SDK-compatible error tracker built with Django and PostgreSQL. That's it — no Kafka, no ClickHouse, no cluster of workers.

You can point any existing Sentry SDK at a GlitchTip instance by changing the DSN. Your client-side code doesn't change. GlitchTip handles error ingestion, grouping, and alerting, though it lacks Sentry's performance monitoring and session replay features.

The trade-off is clear: fewer features, dramatically less infrastructure.

### Highlight.io (discontinued)

Highlight.io bundled error tracking with session replay and logging, closer in scope to Sentry's full product. LaunchDarkly acquired it in April 2025 and the hosted service shut down on February 28, 2026; the product now lives on as LaunchDarkly Observability. The open-source repository is still public but no longer developed as a standalone product, so it isn't a path to start on today.

| Tool               | Min RAM              | Containers                       | Sentry SDK Compatible     | Key Limitation                               |
| ------------------ | -------------------- | -------------------------------- | ------------------------- | -------------------------------------------- |
| Self-hosted Sentry | 16GB + 16GB swap     | 50+                              | Yes (native)              | Heavy infrastructure                         |
| GlitchTip          | 512MB                | 2–3                              | Yes (DSN swap)            | No performance monitoring                    |
| **Temps**          | **Part of platform** | **Same install as your deploys** | **Sentry-compatible DSN** | **Younger project; relies on Sentry's SDKs** |

The error tracking space has bifurcated into two extremes: full-featured but operationally expensive (Sentry), or lightweight but limited (GlitchTip). The missing middle ground is error tracking integrated into something you already run — like your deployment platform — so you get grouping, source maps, and alerting without a dedicated error tracking cluster.

---

## How Do You Build Basic Error Tracking Yourself?

A minimal error tracking system requires roughly 80 lines of code and a [PostgreSQL](https://www.postgresql.org/docs/current/datatype-json.html) table, handling the same core workflow that commercial tools use — capture, normalize, fingerprint, store, alert. It won't replace Sentry's features, but it'll catch and group your errors.

### Step 1: The database schema

```sql
CREATE TABLE errors (
  id SERIAL PRIMARY KEY,
  fingerprint VARCHAR(64) NOT NULL,
  message TEXT NOT NULL,
  stack TEXT,
  level VARCHAR(20) DEFAULT 'error',
  metadata JSONB DEFAULT '{}',
  occurrences INTEGER DEFAULT 1,
  first_seen TIMESTAMPTZ DEFAULT NOW(),
  last_seen TIMESTAMPTZ DEFAULT NOW(),
  resolved BOOLEAN DEFAULT FALSE
);

CREATE INDEX idx_errors_fingerprint ON errors(fingerprint);
CREATE INDEX idx_errors_last_seen ON errors(last_seen DESC);
```

### Step 2: The error capture endpoint

```javascript
import crypto from 'crypto';

function fingerprint(error) {
  // Strip variable data (line numbers, memory addresses) from the message
  const normalized = error.message.replace(/0x[0-9a-f]+/gi, '<addr>')
                                   .replace(/:\d+:\d+/g, ':<line>');
  const key = `${error.type || 'Error'}:${normalized}:${error.topFrame || ''}`;
  return crypto.createHash('sha256').update(key).digest('hex').slice(0, 16);
}

app.post('/api/errors', async (req, res) => {
  const { message, stack, metadata } = req.body;
  const fp = fingerprint({ message, type: metadata?.type, topFrame: stack?.split('\n')[1] });

  await db.query(`
    INSERT INTO errors (fingerprint, message, stack, metadata)
    VALUES ($1, $2, $3, $4)
    ON CONFLICT (fingerprint) DO UPDATE SET
      occurrences = errors.occurrences + 1,
      last_seen = NOW(),
      metadata = errors.metadata || $4
  `, [fp, message, stack, JSON.stringify(metadata)]);

  res.status(202).json({ fingerprint: fp });
});
```

### Step 3: The client-side collector

```javascript
function initErrorTracking(endpoint) {
  const send = (data) => navigator.sendBeacon(endpoint, JSON.stringify(data));

  window.onerror = (msg, source, line, col, err) => {
    send({ message: msg, stack: err?.stack, metadata: { source, line, col, url: location.href } });
  };

  window.addEventListener('unhandledrejection', (e) => {
    send({ message: e.reason?.message || String(e.reason), stack: e.reason?.stack,
           metadata: { type: 'unhandledrejection', url: location.href } });
  });
}

initErrorTracking('/api/errors');
```

### Step 4: Basic alerting via webhook

```javascript
async function checkAndAlert() {
  const newErrors = await db.query(`
    SELECT * FROM errors
    WHERE first_seen > NOW() - INTERVAL '5 minutes' AND occurrences = 1
  `);

  for (const error of newErrors.rows) {
    await fetch(process.env.SLACK_WEBHOOK_URL, {
      method: 'POST',
      body: JSON.stringify({
        text: `New error: ${error.message}\nStack: ${error.stack?.split('\n')[0]}`
      })
    });
  }
}

setInterval(checkAndAlert, 60_000);
```

This DIY approach covers the core of what teams use in Sentry — error capture, grouping, and alerting. What's missing is source map resolution, release tracking, user context enrichment, and a proper dashboard UI. Those features take significantly more engineering effort than the core error pipeline.

This is a solid foundation for a side project or internal tool. But would you want to maintain this in production alongside your actual product? That's where the build-vs-buy calculation gets interesting.

---

## How Does Temps Handle Error Tracking Without a Separate Service?

Temps captures frontend and backend errors through a Sentry-compatible ingestion endpoint that shares infrastructure with its deployment engine, analytics, and session replay — no separate service to install or maintain. You keep the official Sentry SDK; it just sends to your own server. The [error tracking docs](https://temps.sh/docs/error-tracking) cover setup for each language, and the [Temps vs Sentry](https://temps.sh/compare/vs-sentry) page compares the two feature by feature.

This is also what separates Temps from other self-hosted, no-per-seat deployment tools. Coolify and Dokploy are free and self-hosted, but they have no built-in error tracking (Coolify added proxy-level traffic analytics in v4.4, October 2026), so you'd still bolt on GlitchTip or Sentry SaaS and manage a second service, a second login, and a second bill. Temps is the only self-hosted PaaS that bundles deployment, error tracking, analytics, session replay, and uptime monitoring in one install.

**Three things Temps does because it is also the deployment platform:**

1. **The DSN and release are injected for you** — apps deployed on Temps get `SENTRY_DSN` (plus `NEXT_PUBLIC_SENTRY_DSN` / `VITE_SENTRY_DSN` for browser code) and `SENTRY_RELEASE` set to the deployment's commit SHA. On git-based deploys Temps also injects `SENTRY_URL` and `SENTRY_AUTH_TOKEN`, so the official Sentry build plugin uploads source maps to Temps with no extra configuration, keyed to the exact build that produced each error.
2. **Error-to-deploy linkage** — every error is stored with the deployment that was running when it occurred, so a spike after a release points straight at the deploy that caused it. Rolling back is a [manual redeploy](https://temps.sh/docs/rollbacks) of an earlier deployment; Temps doesn't roll back automatically on errors, but a new deploy that fails its health checks never takes traffic.
3. **No extra infrastructure** — one install (`curl -fsSL https://temps.sh/deploy.sh | bash`) gives you analytics, session replay, uptime monitoring, error tracking, and deployments. Self-hosted Temps is free (dual MIT / Apache 2.0 license), running on the server you already pay for, with no per-seat fees and no bandwidth bills.

### Frontend and backend capture

Install the Sentry SDK for your framework (`@sentry/nextjs`, `@sentry/react`, `@sentry/node`, `sentry-python`, and so on) and initialise it with the injected DSN. The SDK hooks into `window.onerror` and `unhandledrejection` in the browser and into uncaught exceptions at the process level on the server, exactly as it does with Sentry. Apps hosted elsewhere can use Temps too: copy the project's DSN from the dashboard into the SDK init.

### Error grouping and deduplication

Temps groups errors by stack-trace fingerprint first. When no fingerprint matches, it compares a hashed-token vector of the error message against existing groups by cosine similarity in pgvector — computed locally, with no external AI call. Each group shows an occurrence counter, first-seen and last-seen timestamps, and affected users.

You can mark a group as resolved — now, in the next release, in a specific release, or automatically after a quiet period. If the error comes back, the group is marked as regressed.

### Alerting

Error alert rules can fire on a new issue, a regression, an error count above a threshold in a time window, a first affected user, or a number of affected users, with a cooldown so a noisy bug doesn't page you every minute. Alerts go to the notification providers you configure (email, Slack or webhook).

### Same dashboard, no extra service

There's no separate error tracking URL or login. Errors appear in the same Temps dashboard where you manage deployments, view analytics, and watch session replays. One tab shows your deploy history, another shows the errors each deploy introduced.

**Related:** For a side-by-side comparison of error tracking tools with pricing, see [Sentry alternatives for error tracking](https://temps.sh/blog/8-best-sentry-alternatives-error-tracking-2026). If seat-based pricing is the problem, see [error tracking without per-seat pricing](https://temps.sh/blog/best-error-tracking-without-per-seat-pricing-2026). For the full observability picture, check [How to Set Up OpenTelemetry Tracing](https://temps.sh/blog/how-to-set-up-opentelemetry-tracing).

---

## Frequently Asked Questions

### How do I set up error tracking without Sentry?

Keep the Sentry SDK and change where it sends data, or build your own. You have three options. First, deploy on [Temps](https://temps.sh/error-tracking): it injects a Sentry-compatible DSN (`SENTRY_DSN`) into your app, so you install the Sentry SDK, initialise it with that variable, and errors, source maps and releases land on your own server. Second, self-host GlitchTip: it implements the same DSN-based ingestion protocol, so you change one config line (`dsn: 'https://<key>@your-glitchtip.com/<id>'`) and keep every existing `@sentry/react` or `@sentry/node` call. Third, build a minimal tracker yourself — the core is \~80 lines: a `window.onerror` handler, a fingerprint function, a PostgreSQL upsert, and a Slack webhook.

### Which self-hosted error tracker should I pick?

Pick Temps if you also want deployments, analytics, session replay and uptime monitoring in the same install; it's free to self-host under MIT / Apache 2.0. Pick GlitchTip if you want a standalone tracker decoupled from your deployment platform — it's free, Sentry-SDK-compatible, and runs on 512MB of RAM. For a comparison focused on pricing models, see [error tracking without per-seat pricing](https://temps.sh/blog/best-error-tracking-without-per-seat-pricing-2026).

### Can you use Sentry's SDK with a self-hosted alternative?

Yes. GlitchTip is fully compatible with Sentry's official SDKs — you swap the DSN to point at your GlitchTip instance and everything works. Temps also accepts a Sentry-compatible DSN for projects that already instrument with `@sentry/react` or `@sentry/node`. Self-hosted Sentry uses its own SDK natively.

### How many errors does a typical production app generate?

It varies widely, and much of it isn't bugs: network timeouts, third-party script failures, browser extension interference, and bot-generated noise all show up as errors. An app that logs a few thousand of those a day would exhaust Sentry's free tier (5,000 errors a month) in a day or two. Measure your volume for a week before choosing a pricing tier to avoid bill shock.

### What's the minimum server to self-host error tracking?

According to the [GlitchTip documentation](https://glitchtip.com/documentation/install), it recommends 512MB of RAM. It needs PostgreSQL 14+ and a single application service, with Valkey/Redis optional. Self-hosted Sentry documents a minimum of 4 CPU cores and 16GB of RAM plus 16GB of swap for its 50+ compose services. For teams that want error tracking without the infrastructure burden, Temps bundles it into the deployment platform you're already running — no additional server required.

### Is DIY error tracking viable for production apps?

For small teams and side projects, a DIY error tracker built on PostgreSQL works surprisingly well. The core logic — capture, fingerprint, store, alert — is straightforward. The challenge comes with scale and features: source map resolution, user session linking, release regression detection, and a usable dashboard all require significant engineering time. Most teams find that the DIY approach works for the first 6 months, then they either adopt a tool or dedicate an engineer to maintaining the system. If error tracking isn't your core product, it probably shouldn't consume your engineering time.

### Is Temps error tracking free?

Yes. Self-hosting Temps is free under a dual MIT / Apache 2.0 license — no per-event fees, no seat limits, no bandwidth bills. Temps Cloud, a hosted control plane for your own servers, is coming soon from $29/month. Either way, error tracking, analytics, session replay, uptime monitoring, and deployments are all included at no extra charge.

---

## Stop Paying Per Error

Error tracking is a solved problem. The core mechanics — global error handlers, fingerprinting, source map resolution — haven't changed meaningfully in years. What has changed is the pricing. SaaS tools now charge per event for something that costs pennies to store in PostgreSQL.

You have real options:

- **GlitchTip** gives you Sentry compatibility on a small VPS with 512MB RAM
- **DIY** covers the basics in \~80 lines of code with no external dependency
- **Temps** includes Sentry-compatible error tracking in the same install that handles deployments, analytics, session replay, and uptime — free when self-hosted (dual MIT / Apache 2.0)

If you're already self-hosting your deployments with Temps — or considering it — error tracking comes built in. No extra service, no DSN to copy, no per-event billing. Errors show up in the same dashboard as your deployments, analytics, and session replays.

```bash
curl -fsSL https://temps.sh/deploy.sh | bash
```

---

Source: https://temps.sh/blog/how-to-set-up-error-tracking-without-sentry · Index of all pages: https://temps.sh/llms.txt
