Self-hosted observability and developer tools platform for Rails applications.
Brainz Lab provides complete observability and developer tools for your Rails apps:
Observability
- Recall - Structured logging with powerful search
- Reflex - Error tracking with smart grouping
- Pulse - APM with distributed tracing
- Beacon - Uptime monitoring and status pages
- Sentinel - Infrastructure and host monitoring
Developer Tools
- Signal - Alerting and notifications hub
- Flux - Feature flags and experiments
- Vault - Secrets management
- Vision - Visual regression testing
curl -fsSL https://raw.githubusercontent.com/brainz-lab/stack/main/install.sh | bash- Docker & Docker Compose
- 4GB RAM minimum (8GB recommended for full stack)
Note: The first start takes ~2 minutes while 36 databases are created and schemas are loaded. Subsequent starts are much faster.
| Service | Port | Subdomain | Description |
|---|---|---|---|
| Recall | 3001 | recall.localhost | Structured logging |
| Reflex | 3002 | reflex.localhost | Error tracking |
| Pulse | 3003 | pulse.localhost | APM & tracing |
| Flux | 3004 | flux.localhost | Feature flags |
| Signal | 3005 | signal.localhost | Alerting hub |
| Vault | 3006 | vault.localhost | Secrets management |
| Beacon | 3007 | beacon.localhost | Uptime monitoring |
| Vision | 3008 | vision.localhost | Visual testing |
| Sentinel | 3009 | sentinel.localhost | Infrastructure monitoring |
| Service | Port | Description |
|---|---|---|
| Traefik | 80, 8080 | Reverse proxy (dashboard on 8080) |
| TimescaleDB | 5432 | PostgreSQL with time-series extensions |
| Redis | 6379 | Cache and job queues |
| MinIO | 9000, 9001 | S3-compatible object storage |
./scripts/start.sh./scripts/stop.sh./scripts/logs.sh # All services
./scripts/logs.sh recall # Single service./scripts/reset.shTraefik routes requests based on subdomain. On macOS, .localhost domains resolve automatically. On Linux, add to /etc/hosts:
127.0.0.1 recall.localhost reflex.localhost pulse.localhost
127.0.0.1 flux.localhost signal.localhost vault.localhost
127.0.0.1 beacon.localhost vision.localhost sentinel.localhost
Then access: http://recall.localhost, http://reflex.localhost, etc.
Access services directly: http://localhost:3001 (Recall), http://localhost:3002 (Reflex), etc.
View routing and health: http://localhost:8080
# Gemfile
gem 'brainzlab'# config/initializers/brainzlab.rb
BrainzLab.configure do |config|
config.secret_key = ENV['BRAINZLAB_SECRET_KEY']
# Self-hosted URLs (via Traefik subdomains)
config.recall_url = ENV['RECALL_URL'] # http://recall.localhost
config.reflex_url = ENV['REFLEX_URL'] # http://reflex.localhost
config.pulse_url = ENV['PULSE_URL'] # http://pulse.localhost
config.signal_url = ENV['SIGNAL_URL'] # http://signal.localhost
config.flux_url = ENV['FLUX_URL'] # http://flux.localhost
config.vault_url = ENV['VAULT_URL'] # http://vault.localhost
endTip: Run
./scripts/setup.shto generate keys and optionally export URLs to your shell profile.
# Logging
BrainzLab::Recall.info("User signed up", user_id: user.id)
# Error tracking (automatic in Rails, or manual)
BrainzLab::Reflex.capture(exception, user: current_user)
# APM (automatic instrumentation)
# Just install the gem and it works!
# Custom metrics
BrainzLab::Pulse.increment("orders.created")
BrainzLab::Pulse.gauge("queue.size", Sidekiq::Queue.new.size)
# Feature flags
if BrainzLab::Flux.enabled?(:new_checkout, user: current_user)
# New checkout flow
end
# Secrets
api_key = BrainzLab::Vault.get("stripe/api_key")
# Alerts
BrainzLab::Signal.trigger("high_error_rate", severity: :critical)Copy .env.example to .env and customize:
cp .env.example .env| Variable | Description | Default |
|---|---|---|
POSTGRES_USER |
Database user | brainzlab |
POSTGRES_PASSWORD |
Database password | brainzlab |
RECALL_PORT |
Recall port | 3001 |
REFLEX_PORT |
Reflex port | 3002 |
PULSE_PORT |
Pulse port | 3003 |
FLUX_PORT |
Flux port | 3004 |
SIGNAL_PORT |
Signal port | 3005 |
VAULT_PORT |
Vault port | 3006 |
BEACON_PORT |
Beacon port | 3007 |
VISION_PORT |
Vision port | 3008 |
SENTINEL_PORT |
Sentinel port | 3009 |
| Variable | Description | Default |
|---|---|---|
RECALL_HOST |
Recall subdomain | recall.localhost |
REFLEX_HOST |
Reflex subdomain | reflex.localhost |
PULSE_HOST |
Pulse subdomain | pulse.localhost |
FLUX_HOST |
Flux subdomain | flux.localhost |
SIGNAL_HOST |
Signal subdomain | signal.localhost |
VAULT_HOST |
Vault subdomain | vault.localhost |
BEACON_HOST |
Beacon subdomain | beacon.localhost |
VISION_HOST |
Vision subdomain | vision.localhost |
SENTINEL_HOST |
Sentinel subdomain | sentinel.localhost |
To use GHCR instead of Docker Hub:
RECALL_IMAGE=ghcr.io/brainz-lab/recall:latest
REFLEX_IMAGE=ghcr.io/brainz-lab/reflex:latest
PULSE_IMAGE=ghcr.io/brainz-lab/pulse:latest
# ... etc for other services- Use a managed database (RDS, Cloud SQL, etc.) or secure your TimescaleDB
- Set up HTTPS - Traefik can handle Let's Encrypt automatically
- Configure proper secrets in
.env - Set up backups for PostgreSQL and MinIO
Set up DNS records pointing to your server:
recall.yourdomain.com → Your server IP
reflex.yourdomain.com → Your server IP
pulse.yourdomain.com → Your server IP
flux.yourdomain.com → Your server IP
signal.yourdomain.com → Your server IP
vault.yourdomain.com → Your server IP
beacon.yourdomain.com → Your server IP
vision.yourdomain.com → Your server IP
sentinel.yourdomain.com → Your server IP
# Database
POSTGRES_USER=brainzlab
POSTGRES_PASSWORD=<strong-password>
# Master keys - leave EMPTY for pre-built images.
# The images include encrypted credentials that match their built-in keys.
# Setting custom master keys will cause "key must be 16 bytes" errors.
RECALL_MASTER_KEY=
REFLEX_MASTER_KEY=
PULSE_MASTER_KEY=
FLUX_MASTER_KEY=
SIGNAL_MASTER_KEY=
VAULT_MASTER_KEY=
BEACON_MASTER_KEY=
VISION_MASTER_KEY=
SENTINEL_MASTER_KEY=
# Secret keys and SDK key (auto-generated by setup.sh)
BRAINZLAB_SECRET_KEY=<generated-key>
# Subdomains (your domain)
RECALL_HOST=recall.yourdomain.com
REFLEX_HOST=reflex.yourdomain.com
PULSE_HOST=pulse.yourdomain.com
FLUX_HOST=flux.yourdomain.com
SIGNAL_HOST=signal.yourdomain.com
VAULT_HOST=vault.yourdomain.com
BEACON_HOST=beacon.yourdomain.com
VISION_HOST=vision.yourdomain.com
SENTINEL_HOST=sentinel.yourdomain.comUpdate traefik/traefik.yml for production with Let's Encrypt:
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: your-email@example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web┌─────────────────────────────────────────────────────────────────┐
│ Your Rails App │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ brainzlab gem │ │
│ │ Recall │ Reflex │ Pulse │ Signal │ Flux │ Vault │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Traefik │
│ (Reverse Proxy + Load Balancer) │
│ recall.* │ reflex.* │ pulse.* │ ... │
└─────────────────────────────────────────────────────────────────┘
│
┌────────────────────────┼────────────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Recall │ │ Reflex │ │ Pulse │ │ Flux │ │ Signal │
│ :3001 │ │ :3002 │ │ :3003 │ │ :3004 │ │ :3005 │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
│ │ │ │ │
└────────────┴────────────┴────────────┴────────────┘
│
┌────────────────────────┼────────────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Vault │ │ Beacon │ │ Vision │ │Sentinel │ │ MinIO │
│ :3006 │ │ :3007 │ │ :3008 │ │ :3009 │ │ :9000 │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └─────────┘
│ │ │ │
└────────────┴────────────┴────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ TimescaleDB │ │ Redis │
│ :5432 │ │ :6379 │
└─────────────┘ └─────────────┘
All services expose a /up endpoint for health checks:
curl http://localhost:3001/up # Recall
curl http://localhost:3002/up # Reflex
# ... etcTraefik dashboard shows service health: http://localhost:8080
Check if ports are in use:
lsof -i :3001 # Check if port is takenIf you see "container name already in use" errors:
docker-compose down --remove-orphans
docker-compose up -dEnsure TimescaleDB is healthy:
docker-compose ps timescaledb
docker-compose logs timescaledbOn first boot, services need ~2 minutes for database setup. Wait and retry:
# Check a specific service
docker-compose logs vault
# Look for "Listening on http://0.0.0.0:3000" to confirm it's readySome services may show seed warnings like NoMethodError or RecordInvalid during first boot. These are non-fatal - the services still start and function correctly.
If something goes wrong and you want a completely fresh start:
./scripts/reset.sh # Removes all data and volumes
./scripts/start.sh # Start freshCheck Traefik dashboard at http://localhost:8080 for:
- Service health (green = healthy)
- Router configuration
- Active routes
docker-compose logs -fFull documentation: docs.brainzlab.ai/self-hosting
- brainzlab-ruby - Ruby SDK
- Recall - Logging service
- Reflex - Error tracking service
- Pulse - APM service
- Flux - Feature flags service
- Signal - Alerting service
- Vault - Secrets service
- Beacon - Uptime monitoring service
- Vision - Visual testing service
- Sentinel - Infrastructure monitoring service
MIT