# DON'T USE THIS FILE, USE docker-compose.yml instead
services:
caddy:
image: caddy:2.10.0
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # Needed for HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile # Mount Caddy config file
- caddy_data:/data # Mount persistent data volume for certs etc.
- caddy_config:/config # Mount persistent config volume
environment:
# Pass domain name for use in Caddyfile
# Email is configured via Caddyfile global options
- DOMAIN_NAME=${DOMAIN_NAME}
depends_on:
- backend
- client
clickhouse:
container_name: clickhouse
image: clickhouse/clickhouse-server:26.3.62.3
volumes:
- clickhouse-data:/var/lib/clickhouse
- /etc/rybbit-backup/clickhouse-s3.xml:/etc/clickhouse-server/config.d/backup-s3.xml:ro
- /etc/rybbit-backup/clickhouse-backup-user.xml:/etc/clickhouse-server/users.d/backup-user.xml:ro
configs:
- source: clickhouse_network
target: /etc/clickhouse-server/config.d/network.xml
- source: clickhouse_server_settings
target: /etc/clickhouse-server/config.d/server_settings.xml
- source: clickhouse_logging
target: /etc/clickhouse-server/config.d/logging_rules.xml
# Profile (user-level) settings are only read from users.d, not config.d
- source: clickhouse_user_settings
target: /etc/clickhouse-server/users.d/user_settings.xml
environment:
- CLICKHOUSE_DB=${CLICKHOUSE_DB:-analytics}
- CLICKHOUSE_USER=${CLICKHOUSE_USER:-default}
- CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD:-frog}
# Lets the backend provision the least-privilege `rybbit_query` user
# (custom SQL queries / dashboards) with SQL at startup.
- CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8123/ping"]
interval: 3s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
ports:
- "8123:8123"
- "9000:9000"
mem_limit: 12g
mem_reservation: 6g
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
postgres:
image: postgres:17.4
container_name: postgres
environment:
- POSTGRES_USER=${POSTGRES_USER:-frog}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-frog}
- POSTGRES_DB=${POSTGRES_DB:-analytics}
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
ports:
- "5432:5432"
redis:
image: redis:8.6.4-alpine
container_name: redis
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-changeme}", "--no-auth-warning", "ping"]
interval: 3s
timeout: 5s
retries: 5
start_period: 5s
restart: unless-stopped
# Backs session tracking, sticky identity, and bot-anomaly counters.
# noeviction keeps those keys from being dropped under memory pressure;
# session keys carry a 30-minute TTL so memory stays bounded by active-user
# count. AOF (everysec) keeps that state durable across restarts.
command:
- redis-server
- --requirepass
- ${REDIS_PASSWORD:-changeme}
- --appendonly
- "yes"
- --appendfsync
- everysec
- --maxmemory-policy
- noeviction
backend:
container_name: backend
build:
context: .
dockerfile: ./server/Dockerfile
environment:
- NODE_ENV=production
- CLICKHOUSE_HOST=${CLICKHOUSE_HOST:-http://clickhouse:8123}
- CLICKHOUSE_DB=${CLICKHOUSE_DB:-analytics}
- CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD:-frog}
- CLICKHOUSE_QUERY_PASSWORD=${CLICKHOUSE_QUERY_PASSWORD:-${CLICKHOUSE_PASSWORD:-frog}}
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
- POSTGRES_DB=${POSTGRES_DB:-analytics}
- POSTGRES_USER=${POSTGRES_USER:-frog}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-frog}
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
- BASE_URL=${BASE_URL}
- DISABLE_SIGNUP=${DISABLE_SIGNUP}
# below is only for rybbit cloud
- CLOUD=${CLOUD}
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
# AppSumo configuration
- APPSUMO_CLIENT_ID=${APPSUMO_CLIENT_ID}
- APPSUMO_CLIENT_SECRET=${APPSUMO_CLIENT_SECRET}
- APPSUMO_API_KEY=${APPSUMO_API_KEY}
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
- GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
- RESEND_API_KEY=${RESEND_API_KEY}
# R2 storage for session replay (cloud only)
- R2_ACCOUNT_ID=${R2_ACCOUNT_ID}
- R2_ACCESS_KEY_ID=${R2_ACCESS_KEY_ID}
- R2_SECRET_ACCESS_KEY=${R2_SECRET_ACCESS_KEY}
- R2_BUCKET_NAME=${R2_BUCKET_NAME:-rybbit}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD:-changeme}
- MAPBOX_TOKEN=${MAPBOX_TOKEN}
- TURNSTILE_SECRET_KEY=${TURNSTILE_SECRET_KEY}
- GOOGLE_REDIRECT_URI=${GOOGLE_REDIRECT_URI}
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
- OPENROUTER_MODEL=${OPENROUTER_MODEL}
- CLUSTER_WORKERS=${CLUSTER_WORKERS:-4}
depends_on:
clickhouse:
condition: service_healthy
postgres:
condition: service_started
redis:
condition: service_healthy
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
ports:
- "3001:3001"
client:
container_name: client
build:
context: .
dockerfile: ./client/Dockerfile
args:
NEXT_PUBLIC_BACKEND_URL: ${BASE_URL}
NEXT_PUBLIC_DISABLE_SIGNUP: ${DISABLE_SIGNUP}
NEXT_PUBLIC_CLOUD: ${CLOUD}
NEXT_PUBLIC_MAPBOX_TOKEN: ${MAPBOX_TOKEN}
NEXT_PUBLIC_TURNSTILE_SITE_KEY: ${TURNSTILE_SITE_KEY}
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${STRIPE_PUBLISHABLE_KEY}
environment:
- NODE_ENV=production
- NEXT_PUBLIC_BACKEND_URL=${BASE_URL}
- NEXT_PUBLIC_DISABLE_SIGNUP=${DISABLE_SIGNUP}
- NEXT_PUBLIC_CLOUD=${CLOUD}
- NEXT_PUBLIC_MAPBOX_TOKEN=${MAPBOX_TOKEN}
- NEXT_PUBLIC_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY}
- NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${STRIPE_PUBLISHABLE_KEY}
depends_on:
- backend
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
ports:
- "3002:3002"
volumes:
clickhouse-data:
postgres-data:
redis-data:
caddy_data: # Persistent volume for Caddy's certificates and state
caddy_config: # Persistent volume for Caddy's configuration cache (optional but good practice)
configs:
clickhouse_network:
content: |
0.0.0.0
# Server-level settings live directly under ; a
# wrapper is not a recognized section and gets silently ignored.
# background_pool_size below ~10 fails the startup sanity check
# (number_of_free_entries_in_pool_to_execute_mutation defaults to 20),
# so the merge pool is left at its default.
clickhouse_server_settings:
content: |
20
524288000
clickhouse_logging:
content: |
warning
true
system
7500
event_date + INTERVAL 7 DAY DELETE
clickhouse_user_settings:
content: |
1
1
1
1
0
0
8000000000
4000000000
4000000000
4
8192
0