Sentry Self-Hosted Error Tracking Installation

Sentry is an open-source error tracking and performance monitoring platform that captures exceptions, slowdowns, and crashes in real time, helping developers reproduce and fix issues faster. This guide covers deploying self-hosted Sentry with Docker, setting up projects, integrating SDKs, configuring alert rules, and tracking releases.

Prerequisites

  • Ubuntu 20.04+ or CentOS 8+ / Rocky Linux 8+
  • Docker and Docker Compose
  • 16 GB RAM minimum (Sentry's self-hosted stack is resource-intensive)
  • 50 GB disk space
  • A domain name with DNS pointing to your server (for email and HTTPS)

Installing Self-Hosted Sentry

Sentry provides an official self-hosted installation script:

# Clone the self-hosted repository
git clone https://github.com/getsentry/self-hosted.git
cd self-hosted

# Check out the latest stable release
git checkout $(git tag --sort=version:refname | tail -1)

# Run the installation script
# This installs Docker, creates config files, and runs migrations (~10 minutes)
sudo ./install.sh

# The install script will prompt you to create an admin user
# Enter your email and password when asked

After installation completes:

# Start all services
docker compose up -d

# Check that all containers are healthy
docker compose ps

# Key services running:
# sentry-web     - Web UI and API (port 9000)
# sentry-worker  - Background task processing
# postgres       - Database
# redis          - Caching and message broker
# clickhouse     - Error event storage (newer versions)
# kafka          - Event ingestion queue
# snuba          - Query layer for ClickHouse

Wait 5-10 minutes for all services to fully start. Check health:

curl http://localhost:9000/_health/

Initial Setup

Access Sentry at http://your-server:9000. Log in with the admin credentials you created during installation.

Configure a reverse proxy with TLS (required for production and SDK integrations):

# /etc/nginx/sites-available/sentry
server {
    listen 80;
    server_name sentry.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name sentry.example.com;

    ssl_certificate /etc/letsencrypt/live/sentry.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sentry.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 300s;
        client_max_body_size 100M;
    }
}

Configure the Sentry URL in .env:

# In self-hosted/.env
SENTRY_URL=https://sentry.example.com

Configure email notifications in .env:

SENTRY_EMAIL_HOST=smtp.gmail.com
SENTRY_EMAIL_PORT=587
SENTRY_EMAIL_USE_TLS=true
[email protected]
SENTRY_EMAIL_PASSWORD=your-app-password
[email protected]

After editing .env, restart:

docker compose restart web worker

Creating Projects and Getting DSNs

  1. In Sentry, go to SettingsProjectsCreate Project
  2. Select your platform (Python, Node.js, React, Go, etc.)
  3. Name the project (e.g., "production-api")
  4. Click Create Project

The DSN (Data Source Name) is displayed. It looks like:

https://[email protected]/1234

For self-hosted, the DSN format is:

https://{public_key}@sentry.example.com/{project_id}

Find your project's DSN:

  • SettingsProjects → select project → SDK SetupDSN

SDK Integration

Python:

pip install sentry-sdk
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration  # or FastApiIntegration

sentry_sdk.init(
    dsn="https://[email protected]/1",
    integrations=[DjangoIntegration()],
    traces_sample_rate=0.1,    # 10% of transactions for performance
    profiles_sample_rate=0.1,  # 10% profiling
    environment="production",
    release="[email protected]",
    send_default_pii=False,    # Don't send IP, username, etc.
)

# Test: this will send a test event
# with sentry_sdk.push_scope() as scope:
#     scope.set_tag("test", True)
#     raise Exception("Test exception")

Node.js / Express:

npm install @sentry/node @sentry/profiling-node
// instrument.js (require before anything else)
const Sentry = require("@sentry/node");
const { nodeProfilingIntegration } = require("@sentry/profiling-node");

Sentry.init({
    dsn: "https://[email protected]/1",
    integrations: [nodeProfilingIntegration()],
    tracesSampleRate: 0.1,
    profilesSampleRate: 0.1,
    environment: process.env.NODE_ENV || "production",
    release: process.env.npm_package_version,
});

// Express error handler (add after routes)
const app = require("express")();
app.use(Sentry.Handlers.requestHandler());
// ... your routes ...
app.use(Sentry.Handlers.errorHandler());

React (frontend):

npm install @sentry/react
// index.js
import * as Sentry from "@sentry/react";

Sentry.init({
    dsn: "https://[email protected]/1",
    integrations: [Sentry.browserTracingIntegration()],
    tracesSampleRate: 0.1,
    replaysSessionSampleRate: 0.1,
    replaysOnErrorSampleRate: 1.0,
    environment: "production",
    release: "[email protected]",
});

Go:

go get github.com/getsentry/sentry-go
package main

import (
    "log"
    "time"
    "github.com/getsentry/sentry-go"
)

func main() {
    err := sentry.Init(sentry.ClientOptions{
        Dsn:              "https://[email protected]/1",
        TracesSampleRate: 0.1,
        Environment:      "production",
        Release:          "[email protected]",
    })
    if err != nil {
        log.Fatalf("sentry.Init: %s", err)
    }
    defer sentry.Flush(2 * time.Second)

    // Capture manually:
    sentry.CaptureException(fmt.Errorf("something went wrong"))
}

Alert Rules and Notifications

Create alert rules in AlertsCreate Alert Rule:

Error alert example:

  • Type: Issues
  • Conditions: An issue is seen more than 10 times in 1h
  • Filter: Environment: production
  • Action: Send email to team, post to #alerts Slack channel

Performance alert example:

  • Type: Metric Alert
  • Metric: Transaction duration P95
  • Threshold: > 2000ms
  • Time window: 5 minutes
  • Action: Notify on-call

Configure Slack integration:

  1. SettingsIntegrationsSlackAdd to Slack
  2. Authorize Sentry to post to your workspace
  3. In alert rules, select Slack as the notification channel

Configure PagerDuty:

  1. SettingsIntegrationsPagerDuty
  2. Enter your PagerDuty integration key
  3. Select services to map to PagerDuty services

Release Tracking and Source Maps

Tag events with release versions to track when bugs were introduced:

# Create a release via the Sentry CLI
# Install the CLI
curl -sL https://sentry.io/get-cli/ | bash
# For self-hosted, set the URL:
export SENTRY_URL=https://sentry.example.com
export SENTRY_AUTH_TOKEN=your-auth-token  # Settings → Auth Tokens
export SENTRY_ORG=your-org-slug
export SENTRY_PROJECT=your-project-slug

# Create release, upload source maps, finalize
sentry-cli releases new "[email protected]"
sentry-cli releases files "[email protected]" upload-sourcemaps ./dist
sentry-cli releases finalize "[email protected]"
sentry-cli releases deploys "[email protected]" new -e production

In your CI/CD pipeline (GitHub Actions):

- name: Create Sentry Release
  env:
    SENTRY_URL: https://sentry.example.com
    SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
    SENTRY_ORG: myorg
    SENTRY_PROJECT: my-app
  run: |
    sentry-cli releases new ${{ github.sha }}
    sentry-cli releases set-commits --auto ${{ github.sha }}
    sentry-cli releases files ${{ github.sha }} upload-sourcemaps ./dist
    sentry-cli releases finalize ${{ github.sha }}
    sentry-cli releases deploys ${{ github.sha }} new -e production

Performance Monitoring

Sentry captures transaction traces automatically with the SDK. View them under Performance:

  • Duration histogram and P50/P95/P99 breakdowns
  • Slow DB Queries - automatically detected slow queries
  • N+1 Issues - detected ORM query problems
  • Transaction waterfall - full span breakdown

Add custom spans to trace business logic:

import sentry_sdk

with sentry_sdk.start_transaction(op="task", name="process_order"):
    with sentry_sdk.start_span(op="db.query", description="fetch order"):
        order = db.fetch_order(order_id)

    with sentry_sdk.start_span(op="http.client", description="payment gateway"):
        result = payment_api.charge(order)

Troubleshooting

Services failing to start:

cd self-hosted
docker compose logs --tail=50 web
docker compose logs --tail=50 worker
# Common: insufficient RAM, PostgreSQL not ready

Events not appearing in Sentry:

# Check if events are being received
docker compose logs relay | tail -20
# Relay handles inbound SDK events

# Test with sentry-cli
sentry-cli send-event -m "Test event" --logfile /dev/stdin <<< "test"

Disk space filling up:

# Check storage usage
docker system df
du -sh self-hosted/

# Adjust retention in Settings → Admin → General Settings
# Default: 90 days for events

Email notifications not working:

# Send a test email
docker compose run --rm web sentry sendtestemail --to [email protected]

Sentry is slow:

# Check resource usage
docker stats

# ClickHouse is the main memory consumer
# Increase Docker memory limit or add more RAM

Conclusion

Self-hosted Sentry provides enterprise-grade error tracking and performance monitoring with full data ownership and no per-event pricing. The SDK integrations for Python, Node.js, React, Go, and dozens of other platforms capture errors automatically with full stack traces, request context, and breadcrumbs. Combined with release tracking and source maps, Sentry correlates production errors to specific code changes and developers, dramatically reducing time to resolution. Plan for at least 16 GB RAM and provision SSD storage for the best performance.