# Light Alternative: GlitchTip This document describes **GlitchTip** as a much lighter alternative to the self-hosted Sentry stack in `infra/sentry/`. GlitchTip is **NOT** the default observability backend — it lacks Performance/Tracing, Session Replay, and OTLP ingest. Use it only when one of the following applies: - The Sentry host is severely RAM-constrained (< 8 GB available) - A throw-away preview deployment needs an error sink in < 5 minutes - You want a backend on the cheapest possible Hetzner instance for a side-project that only cares about error aggregation For the full Sentry stack, see [`sentry-selfhost.md`](./sentry-selfhost.md). ## At a glance | Item | GlitchTip | Self-Hosted Sentry | | --- | --- | --- | | Error events | yes | yes | | Performance / tracing | no | yes | | Session replay | no | yes | | Profiling | no | yes | | OTLP ingest | no | yes (via Relay) | | Sentry-SDK compatibility | yes (errors only) | yes (all features) | | Container count | 4 | ~10 | | RAM | 1–2 GB | 12–16 GB | | Disk (90 d) | 5–10 GB | 40–60 GB | | Upstream image pin | `glitchtip/glitchtip:v6.1.6` | `getsentry/self-hosted` @ `25.12.1` | ## SDK compatibility note GlitchTip implements a subset of the Sentry HTTP ingest API — **errors only**. The official Sentry SDKs (`@sentry/node`, `@sentry/react`, etc.) work without modification for plain error reporting: ```ts Sentry.init({ dsn: process.env.GLITCHTIP_DSN, // looks like a Sentry DSN }); Sentry.captureException(new Error("hello")); ``` **Do not** enable `tracesSampleRate`, `replaysSessionSampleRate`, or `profilesSampleRate` — those payloads are rejected. If you call `Sentry.startTransaction(...)`, GlitchTip silently drops the span data; nothing breaks, but you also do not see traces. For OpenTelemetry ingest (Phase 02 task #33) GlitchTip is **not** a viable backend. ## Compose (light, 4-container) The following Compose file runs on Coolify exactly like the Sentry stack but with 4 services instead of 10. Save under `infra/glitchtip/docker-compose.yml` (NOT committed today; this is the reference document so we can stand it up quickly if needed). ```yaml version: "3.8" networks: default: name: glitchtip-net coolify: external: true services: postgres: image: postgres:15-alpine environment: POSTGRES_DB: ${GLITCHTIP_DB:-glitchtip} POSTGRES_USER: ${GLITCHTIP_DB_USER:-glitchtip} POSTGRES_PASSWORD: ${GLITCHTIP_DB_PASSWORD:?must be set in .env} volumes: - /data/coolify/services/glitchtip/postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD", "pg_isready", "-U", "${GLITCHTIP_DB_USER:-glitchtip}"] interval: 10s retries: 6 valkey: # GlitchTip 6.x uses Valkey (Redis fork) as cache + broker. Pin to v7 # to match Coolify's existing Redis container ABI. image: valkey/valkey:7-alpine volumes: - /data/coolify/services/glitchtip/valkey-data:/data healthcheck: test: ["CMD", "valkey-cli", "ping"] interval: 10s retries: 6 web: image: glitchtip/glitchtip:v6.1.6 depends_on: postgres: condition: service_healthy valkey: condition: service_healthy environment: DATABASE_URL: postgres://${GLITCHTIP_DB_USER:-glitchtip}:${GLITCHTIP_DB_PASSWORD}@postgres:5432/${GLITCHTIP_DB:-glitchtip} REDIS_URL: redis://valkey:6379/0 SECRET_KEY: ${GLITCHTIP_SECRET_KEY:?generate with python -c "import secrets; print(secrets.token_urlsafe(50))"} GLITCHTIP_DOMAIN: ${GLITCHTIP_BASE_URL:-https://glitchtip.boiler.saas-ctrl.com} DEFAULT_FROM_EMAIL: ${GLITCHTIP_FROM_EMAIL:-glitchtip@boiler.saas-ctrl.com} ENABLE_USER_REGISTRATION: "false" # invite-only, matches Sentry hardening ENABLE_ORGANIZATION_CREATION: "false" CELERY_WORKER_AUTOSCALE: "1,3" volumes: - /data/coolify/services/glitchtip/uploads:/code/uploads networks: - default - coolify labels: - "traefik.enable=true" - "traefik.docker.network=coolify" - "traefik.http.routers.glitchtip.rule=Host(`glitchtip.boiler.saas-ctrl.com`)" - "traefik.http.routers.glitchtip.entrypoints=https" - "traefik.http.routers.glitchtip.tls=true" - "traefik.http.routers.glitchtip.tls.certresolver=letsencrypt" - "traefik.http.services.glitchtip.loadbalancer.server.port=8000" worker: image: glitchtip/glitchtip:v6.1.6 command: ./bin/run-celery-with-beat.sh depends_on: postgres: condition: service_healthy valkey: condition: service_healthy environment: DATABASE_URL: postgres://${GLITCHTIP_DB_USER:-glitchtip}:${GLITCHTIP_DB_PASSWORD}@postgres:5432/${GLITCHTIP_DB:-glitchtip} REDIS_URL: redis://valkey:6379/0 SECRET_KEY: ${GLITCHTIP_SECRET_KEY} GLITCHTIP_DOMAIN: ${GLITCHTIP_BASE_URL:-https://glitchtip.boiler.saas-ctrl.com} CELERY_WORKER_AUTOSCALE: "1,3" volumes: - /data/coolify/services/glitchtip/uploads:/code/uploads ``` Note: this Compose includes an implicit `migrate` step. The `web` service of the GlitchTip image runs Django migrations automatically on startup; no separate `migrate` command is required. If you want an explicit one-shot, override with `command: ./manage.py migrate`. ## `.env` template ```dotenv GLITCHTIP_BASE_URL=https://glitchtip.boiler.saas-ctrl.com # Postgres GLITCHTIP_DB=glitchtip GLITCHTIP_DB_USER=glitchtip GLITCHTIP_DB_PASSWORD=replace-me-strong # Django secret — generate with `python -c "import secrets; print(secrets.token_urlsafe(50))"` GLITCHTIP_SECRET_KEY=replace-me-50-chars-urlsafe # Optional: outbound mail. GlitchTip needs this for invitation flows # even though we disable self-signup. GLITCHTIP_FROM_EMAIL=glitchtip@boiler.saas-ctrl.com EMAIL_URL=smtp://smtp.mailjet.net:587/?_user=...&_password=... ``` ## Bootstrap ```bash # 1. DNS: glitchtip.boiler.saas-ctrl.com -> 46.225.5.93 # 2. Fill .env, then: docker compose up -d # 3. First-superuser: docker compose exec web ./manage.py createsuperuser # 4. Log in, create projects, copy DSNs into apps' Coolify secrets. ``` ## When to choose which | Scenario | Recommendation | | --- | --- | | Default production observability | **Sentry** self-hosted | | OpenTelemetry traces required | **Sentry** (GlitchTip cannot ingest OTLP) | | Replay / Performance / Profiling required | **Sentry** | | Throw-away preview environment | GlitchTip | | < 8 GB RAM available | GlitchTip | | EU-only data residency on a budget VPS | GlitchTip (both work, but GlitchTip's resource footprint is dramatically smaller) | ## Migration paths - **Sentry → GlitchTip:** swap the DSN in app `.env`s. Historical events are not migrated (different schema). Decommission the Sentry stack after a 7-day overlap window. - **GlitchTip → Sentry:** same swap, opposite direction. Historical GlitchTip events also do not migrate. Sentry can ingest the GlitchTip DSN format directly.