diff --git a/.gitignore b/.gitignore index 3c370ddf..7b8ab70f 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,7 @@ docs/.vitepress/cache # Claude Code runtime state .claude/*.lock + +# GlitchTip +docker/glitchtip/.env +backups/ diff --git a/Makefile b/Makefile index fffccbdb..31e895f3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help services services-stop api app docs migrate fresh db-shell test test-db-create schema-dump +.PHONY: help services services-stop services-glitchtip-status api app docs migrate fresh db-shell test test-db-create schema-dump # Colors GREEN := \033[0;32m @@ -6,6 +6,10 @@ YELLOW := \033[0;33m CYAN := \033[0;36m NC := \033[0m +# Compose files merged for local dev. Both files share one project so +# Mailpit (bm_mailpit) is reachable from the GlitchTip containers. +COMPOSE_FILES := -f docker-compose.yml -f docker-compose.glitchtip.yml + help: @echo "" @echo "$(GREEN)╔══════════════════════════════════════════════════════════════╗$(NC)" @@ -13,8 +17,9 @@ help: @echo "$(GREEN)╚══════════════════════════════════════════════════════════════╝$(NC)" @echo "" @echo " $(YELLOW)Services (Docker):$(NC)" - @echo " make services Start MySQL, Redis, Mailpit" - @echo " make services-stop Stop all Docker services" + @echo " make services Start MySQL, Redis, Mailpit, GlitchTip" + @echo " make services-stop Stop all Docker services" + @echo " make services-glitchtip-status Tail GlitchTip web container logs" @echo "" @echo " $(YELLOW)Development Servers:$(NC)" @echo " make api Laravel API → http://localhost:8000" @@ -34,21 +39,27 @@ help: services: @echo "$(GREEN)Starting Docker services...$(NC)" - @docker compose up -d + @docker compose $(COMPOSE_FILES) up -d @echo "" @echo "$(GREEN)Services:$(NC)" - @echo " $(CYAN)MySQL:$(NC) localhost:3306 (crewli / secret)" - @echo " $(CYAN)Redis:$(NC) localhost:6379" - @echo " $(CYAN)Mailpit:$(NC) http://localhost:8025" + @echo " $(CYAN)MySQL:$(NC) localhost:3306 (crewli / secret)" + @echo " $(CYAN)Redis:$(NC) localhost:6379" + @echo " $(CYAN)Mailpit:$(NC) http://localhost:8025" + @echo " $(CYAN)GlitchTip:$(NC) http://localhost:8200" @echo "" @echo "$(YELLOW)Waiting for MySQL...$(NC)" @until docker exec bm_mysql mysqladmin ping -h localhost -u root -proot --silent 2>/dev/null; do sleep 1; done @echo "$(GREEN)✓ Ready!$(NC)" + @echo "$(YELLOW)Note:$(NC) GlitchTip web takes ~60s on first boot (migrations)." + @echo " Tail logs with: $(CYAN)make services-glitchtip-status$(NC)" services-stop: - @docker compose down + @docker compose $(COMPOSE_FILES) down @echo "$(GREEN)✓ Services stopped$(NC)" +services-glitchtip-status: + @docker compose $(COMPOSE_FILES) logs -f glitchtip-web + api: @echo "$(GREEN)Starting Laravel API → http://localhost:8000$(NC)" @cd api && php artisan serve diff --git a/docker-compose.glitchtip.yml b/docker-compose.glitchtip.yml new file mode 100644 index 00000000..0fa58750 --- /dev/null +++ b/docker-compose.glitchtip.yml @@ -0,0 +1,85 @@ +# GlitchTip — self-hosted error tracking (Sentry-protocol compatible). +# +# This file is portable: it runs standalone on the production monitoring +# host AND merges into the local Crewli dev stack via: +# +# docker compose -f docker-compose.yml -f docker-compose.glitchtip.yml up -d +# +# `make services` encapsulates the merge for local development. +# +# All configuration comes from docker/glitchtip/.env via env_file. Copy +# docker/glitchtip/.env.example to docker/glitchtip/.env on first run. +# +# Per RFC-WS-7-OBSERVABILITY §3.1. See dev-docs/GLITCHTIP.md for the +# operations runbook (boot, project provisioning, DSN handling, backup, +# restore). + +services: + glitchtip-postgres: + image: postgres:16-alpine + container_name: glitchtip-postgres + env_file: + - ./docker/glitchtip/.env + volumes: + - glitchtip_postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d glitchtip"] + interval: 5s + timeout: 5s + retries: 10 + restart: unless-stopped + + glitchtip-redis: + image: valkey/valkey:7-alpine + container_name: glitchtip-redis + volumes: + - glitchtip_redis_data:/data + healthcheck: + test: ["CMD", "valkey-cli", "ping"] + interval: 5s + timeout: 5s + retries: 10 + restart: unless-stopped + + glitchtip-web: + image: glitchtip/glitchtip:6.1.6 + container_name: glitchtip-web + depends_on: + glitchtip-postgres: + condition: service_healthy + glitchtip-redis: + condition: service_healthy + env_file: + - ./docker/glitchtip/.env + command: ["sh", "-c", "./bin/run-migrate.sh && ./bin/run-web.sh"] + ports: + - "127.0.0.1:8200:8000" + volumes: + - glitchtip_uploads:/code/uploads + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/api/0/', timeout=4).status==200 else 1)"] + interval: 10s + timeout: 5s + retries: 6 + start_period: 60s + restart: unless-stopped + + glitchtip-worker: + image: glitchtip/glitchtip:6.1.6 + container_name: glitchtip-worker + command: ./bin/run-celery-with-beat.sh + depends_on: + glitchtip-postgres: + condition: service_healthy + glitchtip-redis: + condition: service_healthy + env_file: + - ./docker/glitchtip/.env + volumes: + - glitchtip_uploads:/code/uploads + restart: unless-stopped + +volumes: + glitchtip_postgres_data: + glitchtip_redis_data: + glitchtip_uploads: diff --git a/docker/glitchtip/.env.example b/docker/glitchtip/.env.example new file mode 100644 index 00000000..1b1461c6 --- /dev/null +++ b/docker/glitchtip/.env.example @@ -0,0 +1,62 @@ +# GlitchTip — environment configuration +# +# Local development: cp .env.example .env (the dev defaults below are +# functional out of the box — no edits required for `make services`). +# +# ⚠️ PRODUCTION CHECKLIST (deploying to monitoring.hausdesign.nl): +# - Regenerate SECRET_KEY with: +# python -c "import secrets; print(secrets.token_urlsafe(50))" +# - Set POSTGRES_PASSWORD to a strong random value from the 1Password +# vault. The password embedded in DATABASE_URL MUST match. +# - Set EMAIL_URL to the real SMTP relay (smtp+tls://USER:PASS@HOST:PORT). +# - Set GLITCHTIP_DOMAIN=https://monitoring.hausdesign.nl (HTTPS, no +# trailing slash). +# - Set DEFAULT_FROM_EMAIL to a real sender on the hausdesign.nl domain. +# - Never commit the production .env. Keep it on the host only. + +# === GlitchTip core === +# Generate a real value for production: see header for the python one-liner. +SECRET_KEY=dev-only-not-for-production-use + +# Postgres connection. Password MUST match POSTGRES_PASSWORD below. +DATABASE_URL=postgres://postgres:devsecret@glitchtip-postgres:5432/glitchtip + +# Valkey/Redis connection (Celery broker + result backend). +REDIS_URL=redis://glitchtip-redis:6379/0 + +# Internal listen port for the GlitchTip web container. +PORT=8000 + +# Public-facing URL of the GlitchTip web UI. +# Dev: http://localhost:8200 +# Prod: https://monitoring.hausdesign.nl +GLITCHTIP_DOMAIN=http://localhost:8200 + +# Default sender address for outbound mail (alerts, password resets, …). +# Dev: glitchtip@localhost.dev +# Prod: glitchtip@hausdesign.nl +DEFAULT_FROM_EMAIL=glitchtip@localhost.dev + +# Outbound SMTP relay. +# Dev: smtp://bm_mailpit:1025 (alerts visible at http://localhost:8025) +# Prod: smtp+tls://USER:PASSWORD@HOST:PORT +EMAIL_URL=smtp://bm_mailpit:1025 + +# === Registration (locked down — same in dev and prod) === +# Bert is the only user; first admin is created via: +# docker exec -it glitchtip-web ./manage.py createsuperuser +ENABLE_USER_REGISTRATION=False +ENABLE_OPEN_USER_REGISTRATION=False + +# === Worker tuning === +# Celery autoscale: min,max workers. 1,3 is the production default. +CELERY_WORKER_AUTOSCALE=1,3 + +# Recycle each worker after N tasks to bound memory growth. +CELERY_WORKER_MAX_TASKS_PER_CHILD=10000 + +# === Postgres (consumed only by the glitchtip-postgres service) === +POSTGRES_USER=postgres +# MUST match the password embedded in DATABASE_URL above. +POSTGRES_PASSWORD=devsecret +POSTGRES_DB=glitchtip