Skip to content
Go back

Sentry Self-Hosted for Application Errors

By SumGuy 11 min read
Sentry Self-Hosted for Application Errors

Your App Is Lying to You

Your hobby project runs fine — until it doesn’t. You deploy to a VPS, it starts up, and you walk away feeling accomplished. Then three days later you scroll logs and find a wall of Python tracebacks from Tuesday at 2 AM when a user hit that one endpoint you forgot to test with empty input.

Congratulations. You’ve been flying blind.

stdout is not error tracking. Grepping logs is not error tracking. Silently swallowing exceptions in a try/except block with a pass is definitely not error tracking. Real error tracking means: the moment something breaks, you know exactly what broke, where, with what data, and how many times. That’s Sentry.

The cloud version is great. It’s also $26/month before you start adding teammates. If you’re running five personal projects, that math doesn’t work out. The self-hosted version exists, and it’s free — but it comes with its own tax.

Let’s talk about both.


What Sentry Self-Hosted Actually Is

The getsentry/self-hosted repo is an install.sh script that pulls down a Docker Compose stack with roughly 30 services. That’s not an exaggeration.

Here’s a sample of what you’re dealing with:

When it works, it’s genuinely impressive. You get the full Sentry product — error grouping, performance monitoring, releases, source maps, alert rules, the whole UI. When something breaks, you’re debugging a distributed system on a Friday evening.

Resource Reality

Sentry’s own docs say 8 GB RAM minimum. That’s technically true — it’ll start. In practice, 8 GB is the floor where it runs okay with no load. If you’re actually sending errors to it, or you have more than one project, plan for 16 GB minimum. ClickHouse alone will eat memory.

Disk is the other one. ClickHouse stores raw events, and it stores a lot of them. Start with 50 GB of dedicated disk, separate from your OS disk if possible. Enable data retention limits early — otherwise ClickHouse grows without mercy.

CPU: 4 cores minimum, 8 comfortable.

If you don’t have this lying around, Sentry self-hosted is not for your $5 VPS.

The License Situation

Sentry switched to the Functional Source License (FSL) in October 2023. The short version for self-hosters:

For personal homelab use and small teams running it internally, FSL is fine. You’re not selling anything. Don’t spend more than five minutes worrying about it.


Installing It

Prerequisites

Terminal window
# Docker + Docker Compose v2 installed
# At least 16GB RAM, 50GB+ disk
# Open port 9000 (or whatever you proxy to)
git clone https://github.com/getsentry/self-hosted.git
cd self-hosted

The install script handles the rest — generating configs, pulling images, running migrations, and creating the initial admin user.

Terminal window
sudo ./install.sh

This takes a while. Kafka and ClickHouse images are large. On a decent connection, budget 15-20 minutes.

When it finishes, you’ll be prompted to create an admin account. Do it. Then:

Terminal window
docker compose up -d

The UI comes up at http://localhost:9000. Put a reverse proxy in front of it — Caddy or Nginx — and enable HTTPS before you do anything else.

Caddy Reverse Proxy

Caddyfile
sentry.yourdomain.com {
reverse_proxy localhost:9000
}

That’s it. Caddy handles certs automatically. Change your SENTRY_BIND in .env if port 9000 conflicts with something else.

The .env File

The install script generates a .env file in the self-hosted/ directory. Key things to set:

.env
COMPOSE_PROJECT_NAME=sentry
SENTRY_BIND=127.0.0.1:9000
# Mail settings — wire up your SMTP relay here
SENTRY_EMAIL_HOST=smtp.yourprovider.com
SENTRY_EMAIL_PORT=587
SENTRY_EMAIL_USER=alerts@yourdomain.com
SENTRY_EMAIL_PASSWORD=yourpassword
SENTRY_SERVER_EMAIL=alerts@yourdomain.com
SENTRY_EMAIL_USE_TLS=true

Without mail configured, you can’t reset passwords or receive alert emails.


Setting Up Your First Project

Log into the UI, create an organization (call it whatever), then:

  1. Projects → New Project
  2. Pick your platform (Python, Node.js, React, etc.)
  3. Copy the DSN — it looks like https://<key>@sentry.yourdomain.com/<project-id>

That DSN is the only thing your application needs.


SDK Setup

Python

Terminal window
pip install sentry-sdk
main.py
import sentry_sdk
sentry_sdk.init(
dsn="https://yourkey@sentry.yourdomain.com/1",
# Capture 100% of errors, 10% of transactions
traces_sample_rate=0.1,
# Attach request data, user context, etc.
send_default_pii=True,
environment="production",
release="myapp@1.4.2",
)

That’s the entire setup for a Python app. Sentry monkey-patches exception handling automatically — any unhandled exception gets captured and sent.

If you use Flask or FastAPI, add the integration:

app.py
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
from flask import Flask
sentry_sdk.init(
dsn="https://yourkey@sentry.yourdomain.com/1",
integrations=[FlaskIntegration()],
traces_sample_rate=0.1,
)
app = Flask(__name__)

Django has its own integration, same pattern. Celery tasks, SQLAlchemy queries, Redis calls — they all get wrapped automatically when you add the relevant integration.

JavaScript / Node

Terminal window
npm install @sentry/node
# or for browser
npm install @sentry/browser
instrument.js
const Sentry = require("@sentry/node");
Sentry.init({
dsn: "https://yourkey@sentry.yourdomain.com/1",
tracesSampleRate: 0.1,
environment: process.env.NODE_ENV,
release: process.env.APP_VERSION,
});

For React:

main.tsx
import * as Sentry from "@sentry/react";
Sentry.init({
dsn: "https://yourkey@sentry.yourdomain.com/1",
integrations: [
Sentry.browserTracingIntegration(),
Sentry.replayIntegration({
maskAllText: false,
blockAllMedia: false,
}),
],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});

Session replay is the premium feature that feels like magic — it records what the user was doing when they triggered the error, so you can watch a 30-second clip instead of guessing.


Source Maps (JavaScript)

If you’re shipping minified JavaScript, your stack traces look like:

TypeError: Cannot read properties of undefined (reading 'map')
at n (main.abc123.js:1:42891)

That’s useless. Source maps fix this. When you upload your build’s source maps to Sentry, those cryptic main.abc123.js:1:42891 references get translated back to your actual file and line number.

Upload During CI/CD

Terminal window
# Install the Sentry CLI
npm install -g @sentry/cli
# Configure it
export SENTRY_URL=https://sentry.yourdomain.com
export SENTRY_AUTH_TOKEN=your_api_token
export SENTRY_ORG=your-org-slug
export SENTRY_PROJECT=your-project-slug
# After your build step
sentry-cli releases new "$RELEASE_VERSION"
sentry-cli releases files "$RELEASE_VERSION" upload-sourcemaps ./dist --url-prefix "~/"
sentry-cli releases finalize "$RELEASE_VERSION"

Your API token comes from Sentry UI → Settings → Auth Tokens → Create New Token. Give it project:releases scope minimum.

The key thing is that release in your SDK init must match the release you created via CLI. They’re strings — use a git SHA or semantic version, consistently.


Performance Monitoring (Transactions)

Sentry can track how long your requests take, which database queries are slow, and where time is spent across services. This is the “performance” tab.

It’s genuinely useful. It’s also resource-intensive on the self-hosted side because transaction data goes through Kafka and ClickHouse like everything else — just more of it.

Honest take: set traces_sample_rate to 0.05–0.1 max. At 1.0 (every request), a busy app will feed Sentry faster than your ClickHouse can handle. Start low, bump it up if you want more data.

If you’re running a personal project with light traffic, 0.1 is plenty. If you just want errors and don’t care about performance tracing, set it to 0.0 and skip it entirely. Error tracking and performance monitoring are independent — you don’t need both.


Alerts That Actually Work

An error tracker without alerts is just a log viewer with a nicer UI. Sentry’s alert system is legitimately good.

Setting Up Slack Alerts

In Sentry: Settings → Integrations → Slack → Add to Slack.

Then create an alert rule: Alerts → Create Alert → Issues.

Sample rule: “Notify Slack when any new issue is seen for the first time.”

alert-rule-config.json
{
"name": "New Issues - Slack",
"conditions": [
{"id": "sentry.rules.conditions.first_seen_event.FirstSeenEventCondition"}
],
"actions": [
{
"id": "sentry.integrations.slack.notify_action.SlackNotifyServiceAction",
"workspace": "YOUR_WORKSPACE_ID",
"channel": "#alerts-dev",
"tags": ""
}
],
"frequency": 30
}

For Discord, use the webhook integration — same concept, different integration.

Fingerprinting Rules

Sentry groups errors by fingerprint — by default it groups similar stack traces together. Sometimes it groups things that are actually different issues (same exception type, different root cause). You can override this.

In your project settings → Issue Grouping → Fingerprinting Rules:

fingerprinting-rules
# Group by message text, not just exception type
message:*timeout* -> timeout-issues
# Separate DB connection errors from query errors
stack.function:connect_to_database -> db-connection-failure

This takes five minutes to set up and saves you from one grouped issue that’s actually 12 different bugs.


Backup: Don’t Lose Your Event History

Sentry self-hosted has two data stores you need to back up:

PostgreSQL — your org settings, users, projects, alert rules, saved searches. Small, critical.

Terminal window
# From your self-hosted directory
docker compose exec -T postgres pg_dump -U postgres sentry | gzip > sentry_postgres_$(date +%Y%m%d).sql.gz

ClickHouse — raw event data. Large, important if you care about historical errors.

Terminal window
docker compose exec clickhouse clickhouse-client \
--query "BACKUP DATABASE sentry TO Disk('backups', 'sentry_$(date +%Y%m%d)')"

ClickHouse backup requires the backup disk to be configured. Honestly: if you lose event history, you lose event history. The more critical backup is Postgres — that’s your config and grouping data. Events are useful, but they’re also ephemeral by nature.

Set a retention period in Sentry UI: Settings → Admin → General → Event Retention. 90 days is plenty for most personal projects. ClickHouse will thank you.


Lighter Alternatives (When Sentry Is Too Heavy)

Let’s be honest: not everyone has a 16 GB RAM server sitting around for error tracking. There are real alternatives.

GlitchTip

GlitchTip is the most mature drop-in alternative. It’s built on Django, PostgreSQL, and Redis — no Kafka, no ClickHouse. Much smaller footprint. It implements the Sentry SDK API, so your existing SDK setup works without changes.

docker-compose.yml
version: "3.8"
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: glitchtip
POSTGRES_USER: glitchtip
POSTGRES_PASSWORD: changeme
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
web:
image: glitchtip/glitchtip:latest
depends_on:
- postgres
- redis
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://glitchtip:changeme@postgres/glitchtip
REDIS_URL: redis://redis:6379/0
SECRET_KEY: your-secret-key-change-this
EMAIL_URL: smtp://user:pass@smtp.host:587
GLITCHTIP_DOMAIN: https://glitchtip.yourdomain.com
DEFAULT_FROM_EMAIL: alerts@yourdomain.com
worker:
image: glitchtip/glitchtip:latest
command: ./bin/run-celery-with-beat.sh
depends_on:
- postgres
- redis
environment:
DATABASE_URL: postgresql://glitchtip:changeme@postgres/glitchtip
REDIS_URL: redis://redis:6379/0
SECRET_KEY: your-secret-key-change-this
volumes:
postgres_data:

GlitchTip runs comfortably in 2–4 GB RAM. No Kafka. No ClickHouse. No 30-service compose stack. The tradeoff: no performance monitoring, no session replay, no source map symbolication. It’s pure error tracking — which is honestly what most people need.

Bugsink

Bugsink is even lighter — a single Django process, SQLite-by-default, Sentry API compatible. If you’re running it for one project and want something that starts in 30 seconds, Bugsink works. It’s not going to handle serious volume, but for personal projects it’s fine.

Highlight.io

Highlight.io is the full-stack option — errors, logs, session replay, performance. Self-hosted via Docker Compose, Sentry API compatible for error ingestion. More resources than GlitchTip but less than full Sentry, and you get session replay which Sentry OSS doesn’t include.


When to Just Pay for SaaS Sentry

Here’s the honest version of this conversation:

If you’re a small team shipping something real, the hosted Sentry free tier (5K errors/month) or the Team plan is probably the right call. You get:

Babysitting a Kafka cluster is not engineering leverage. If your team has two developers trying to ship features, spending an afternoon debugging why Celery workers are OOM-killed is not a good use of time.

Self-hosted Sentry makes sense when:

If you’re in that last category — welcome. Kafkais just another thing to learn.


The Bottom Line

Sentry self-hosted is genuinely powerful. The full product runs on your hardware, your data stays yours, and it costs nothing beyond compute. The install is straightforward; the operational weight is not trivial.

If you have 16 GB of RAM to spare and want the full experience — error grouping, performance traces, source maps, alert rules, session replay — run the official getsentry/self-hosted stack.

If you want 90% of the value at 20% of the cost, run GlitchTip. It’s Sentry-compatible, tiny, and you’ll have it running before dinner.

Either way: stop shipping apps that log errors to stdout and call it monitoring. Your Tuesday self deserves better than finding out Wednesday morning.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
ko vs Jib vs Buildpacks
Next Post
Cosign Keyless: Sign Without Keys

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts