Claude
Skills
Sign in
Back

sentry-selfhosted

Included with Lifetime
$97 forever

Sentry self-hosted — error tracking, performance monitoring, and crash reporting, hosted on your own infrastructure (no third-party data sharing). Critical for privacy-respecting apps (wallets, healthcare, finance) where exfil to SaaS is unacceptable. Covers Docker Compose self-host install, SDKs (Rust, Kotlin/Android, Swift/iOS, JS), event sampling + scrubbing for PII redaction, source maps, release health, alert rules. Privacy patterns: opt-in only, route via Tor, scrub addresses/balances. USE WHEN: user mentions "Sentry", "Sentry self-hosted", "sentry-rust", "sentry-android", "sentry-cocoa", "Sentry Relay", "release health", "sentry beforeSend", "self-hosted error tracking", "GlitchTip" (lightweight Sentry alternative) DO NOT USE FOR: Logging - use `observability/rust-tracing` or platform loggers DO NOT USE FOR: Metrics (Prometheus) - use Prometheus skill DO NOT USE FOR: APM tracing only - use `observability/rust-tracing` (OTel) DO NOT USE FOR: Cloud Sentry SaaS - this skill focuses on self-hosted privacy

Backend & APIs

What this skill does

# Sentry Self-Hosted

> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `sentry`.

## Why Self-Host

For wallet apps (BHODL-style) and any privacy-respecting product, sending crash data to a third party is a non-starter:

- Crash payloads can contain user data, addresses, balances
- SaaS retention policies vary; hard to audit
- Regulated environments (HIPAA, finance) often disallow third-party telemetry
- Users distrust apps that phone home

Self-hosted Sentry:
- Full control over retention, access, encryption
- Can run on private network or Tor onion
- Open source (under BSL → Apache 2 after 4 years)
- Same SDKs as cloud Sentry
- **Opt-in only** for sensitive apps — never on by default

For BHODL: Sentry is the right choice **if** crash reporting is enabled by user opt-in, telemetry routes via Tor, and PII is scrubbed.

## Alternatives

| Tool | Comparison |
|---|---|
| **GlitchTip** | Lightweight Sentry-API-compatible alternative, MIT licensed, simpler ops. Uses same SDKs. Best for small/medium apps. |
| **Bugsnag self-hosted** | Closed source after acquisition; not recommended |
| **Custom backend** | Receive raw events, store in DB. Lots of work. |
| **Cloud Sentry** | Easy setup, SaaS — wrong for privacy-respecting apps |

For BHODL-scale: **GlitchTip** if Sentry is overkill. Sentry self-hosted if you need full feature set (release health, performance, distributed tracing).

## Self-Host Setup (Docker Compose)

Sentry official self-host: https://github.com/getsentry/self-hosted

```bash
git clone https://github.com/getsentry/self-hosted.git sentry
cd sentry
git checkout 24.10.0                              # latest stable

# Requires: Docker + Docker Compose, ≥4GB RAM, ≥20GB disk
./install.sh
```

Installation creates:
- ClickHouse (event storage)
- Postgres (metadata)
- Redis (queues)
- Kafka (event ingestion buffer)
- Sentry web + worker

```bash
docker compose up -d
docker compose run --rm web createuser   # create admin
```

Access at `http://localhost:9000`.

For production: front with reverse proxy (Caddy, nginx), TLS via Let's Encrypt, restrict access.

### Resource Footprint

- **Minimum**: 4GB RAM, 4 vCPU, 20GB disk
- **Recommended**: 8GB RAM, 8 vCPU, 100GB+ disk for retention
- **Heavy**: 32GB RAM for high-volume apps

For low-volume wallet app: 4GB VPS works fine.

### GlitchTip (Lighter Alternative)

```bash
# docker-compose.yml
services:
  glitchtip:
    image: glitchtip/glitchtip:latest
    environment:
      DATABASE_URL: postgres://glitchtip:secret@postgres/glitchtip
      SECRET_KEY: <random-32-bytes>
      EMAIL_URL: consolemail://
    ports: ["8000:8000"]
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: glitchtip
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: glitchtip
    volumes: ["pgdata:/var/lib/postgresql/data"]
volumes: { pgdata: }
```

GlitchTip uses Sentry's protocol — all SDKs work unchanged. ~512MB RAM.

## Rust SDK

```toml
[dependencies]
sentry = "0.34"
sentry-tracing = "0.34"                            # bridge from tracing
```

```rust
fn main() {
    let _guard = sentry::init(sentry::ClientOptions {
        dsn: "https://[email protected]/1".parse().ok(),
        release: sentry::release_name!(),
        environment: Some("production".into()),
        sample_rate: 1.0,                          // 100% errors
        traces_sample_rate: 0.1,                   // 10% performance
        send_default_pii: false,                    // CRITICAL: never send PII
        before_send: Some(Arc::new(|event| {
            // Scrub addresses, balances, etc.
            Some(scrub_event(event))
        })),
        ..Default::default()
    });

    // Bridge tracing events to Sentry
    let subscriber = tracing_subscriber::registry()
        .with(tracing_subscriber::fmt::layer())
        .with(sentry_tracing::layer());
    tracing::subscriber::set_global_default(subscriber).unwrap();

    // App
    if let Err(e) = run_app() {
        sentry::capture_error(&*e);
    }
}

fn scrub_event(mut event: sentry::protocol::Event<'static>) -> sentry::protocol::Event<'static> {
    use sentry::protocol::Value;

    // Remove PII fields
    event.user = None;
    event.server_name = None;

    // Scrub message
    event.message = event.message.map(|m| scrub_addresses(&m));

    // Scrub breadcrumbs
    for crumb in &mut event.breadcrumbs {
        crumb.message = crumb.message.as_ref().map(|m| scrub_addresses(m));
        crumb.data.retain(|k, _| !is_sensitive_key(k));
    }

    event
}

fn scrub_addresses(s: &str) -> String {
    // Replace bc1q... and similar with [REDACTED]
    let re = regex::Regex::new(r"\b(bc1[a-z0-9]{38,42}|[13][a-zA-Z0-9]{25,34})\b").unwrap();
    re.replace_all(s, "[REDACTED_ADDR]").to_string()
}

fn is_sensitive_key(k: &str) -> bool {
    matches!(k, "wallet_id" | "address" | "balance" | "seed" | "key" | "txid")
}
```

### Capturing Errors Manually

```rust
match risky_operation() {
    Ok(v) => v,
    Err(e) => {
        sentry::with_scope(
            |scope| {
                scope.set_tag("operation", "wallet_sync");
                scope.set_level(Some(sentry::Level::Warning));
            },
            || sentry::capture_error(&e),
        );
        return Err(e);
    }
}
```

### Performance Monitoring

```rust
let tx = sentry::start_transaction(sentry::TransactionContext::new("wallet.sync", "task"));
sentry::configure_scope(|s| s.set_span(Some(tx.clone().into())));

// ... work ...

tx.finish();
```

## Android SDK (Kotlin)

```kotlin
// app/build.gradle.kts
dependencies {
    implementation("io.sentry:sentry-android:7.18.0")
    implementation("io.sentry:sentry-android-fragment:7.18.0")    // optional
    implementation("io.sentry:sentry-compose:7.18.0")              // Compose
    implementation("io.sentry:sentry-android-okhttp:7.18.0")       // network
}
```

`AndroidManifest.xml`:
```xml
<application>
    <meta-data android:name="io.sentry.dsn" android:value="https://[email protected]/1" />
    <meta-data android:name="io.sentry.environment" android:value="production" />
    <meta-data android:name="io.sentry.send-default-pii" android:value="false" />
</application>
```

For programmatic init (recommended for opt-in pattern):

```kotlin
class BHODLApp : Application() {
    override fun onCreate() {
        super.onCreate()

        if (prefs.getBoolean("crash_reports_enabled", false)) {
            SentryAndroid.init(this) { options ->
                options.dsn = "https://[email protected]/1"
                options.environment = "production"
                options.tracesSampleRate = 0.1
                options.isSendDefaultPii = false
                options.beforeSend = SentryOptions.BeforeSendCallback { event, hint ->
                    scrubEvent(event)
                }
                options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { breadcrumb, hint ->
                    scrubBreadcrumb(breadcrumb)
                }
            }
        }
    }
}

private fun scrubEvent(event: SentryEvent): SentryEvent {
    event.user = null
    event.serverName = null
    event.message?.message?.let { event.message?.message = scrubAddresses(it) }
    return event
}
```

For Compose:

```kotlin
@Composable
fun App() {
    SentryTraced(tag = "App") {
        // your composables — auto-traced
    }
}
```

## iOS SDK (Swift)

```swift
// Package.swift
.package(url: "https://github.com/getsentry/sentry-cocoa.git", from: "8.41.0")
```

```swift
import Sentry

@main
struct BHODLApp: App {
    init() {
        if UserDefaults.standard.bool(forKey: "crash_reports_enabled") {
            SentrySDK.start { options in
                options.dsn = "https://[email protected]/1"
                options.environment = "production"
                options.tracesSampleRate = 0.1
                options.sendDefaultPii = false
                options.beforeSend = { event in
                    self.scrubEvent(event)
       

Related in Backend & APIs