rybbit Docker & Self-Hosted Deployment

GitHub

Rybbit - open-source and privacy-friendly alternative to Google Analytics that is 10x more intuitive.

13,153 stars TypeScript TypeScript 97 files ยท ~171,874 tokens #analytics#google-analytics#product-analytics#web-analytics
RAW Compose
1. Pull Image (DOCKERHUB) docker pull clickhouse/clickhouse-server
2. Docker Run (1-Click Launch) docker run -d --name rybbit -p 80:80 --restart always clickhouse/clickhouse-server:26.3.17.4
3. Podman Run podman run -d --name rybbit -p 80:80 --restart always clickhouse/clickhouse-server:26.3.17.4
4. Skopeo Inspect (Zero-Pull Manifest) skopeo inspect docker://docker.io/clickhouse/clickhouse-server:26.3.17.4
5. ORAS Pull (OCI Artifact) oras pull clickhouse/clickhouse-server:26.3.17.4
6. Compose Deployment docker compose up -d
docker-compose.yml
Download .yml
services:
  caddy:
    profiles: ["with-webserver"]
    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.17.4
    volumes:
      - clickhouse-data:/var/lib/clickhouse
    configs:
      - source: clickhouse_network
        target: /etc/clickhouse-server/config.d/network.xml
      - source: clickhouse_logging
        target: /etc/clickhouse-server/config.d/logging_rules.xml
      - source: clickhouse_resource_limits
        target: /etc/clickhouse-server/config.d/resource_limits.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:
      # Localhost-only: reach from outside via SSH tunnel (ssh -N -L 8123:localhost:8123 <host>)
      - "127.0.0.1:8123:8123"

  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
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 3s
      timeout: 5s
      retries: 5
      start_period: 10s
    restart: unless-stopped
    ports:
      # Localhost-only: reach from outside via SSH tunnel
      - "127.0.0.1:5432:5432"

  redis:
    image: redis:8.6.4-alpine
    container_name: redis
    ports:
      # Localhost-only: reach from outside via SSH tunnel
      - "127.0.0.1:6379:6379"
    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:
    image: ghcr.io/rybbit-io/rybbit-backend:${IMAGE_TAG:-latest}
    container_name: backend
    build:
      context: .
      dockerfile: ./server/Dockerfile
    ports:
      - "${HOST_BACKEND_PORT:-127.0.0.1:3001:3001}"
    environment:
      - NODE_ENV=production
      - 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}
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - REDIS_PASSWORD=${REDIS_PASSWORD:-changeme}
      - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
      - BASE_URL=${BASE_URL}
      - DISABLE_SIGNUP=${DISABLE_SIGNUP}
      - DISABLE_TELEMETRY=${DISABLE_TELEMETRY}
      - MAPBOX_TOKEN=${MAPBOX_TOKEN}
      - CLUSTER_WORKERS=${CLUSTER_WORKERS:-4}
      - DEPLOYMENT=${DEPLOYMENT}
      - LITE_DASHBOARD=${LITE_DASHBOARD}
      - DASHBOARD_CACHE_TTL_SECONDS=${DASHBOARD_CACHE_TTL_SECONDS:-30}
      - OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
      - OPENROUTER_MODEL=${OPENROUTER_MODEL}
    depends_on:
      clickhouse:
        condition: service_healthy
      postgres:
        condition: service_started
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3001/api/health"]
      interval: 3s
      timeout: 5s
      retries: 5
      start_period: 10s
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"

  client:
    image: ghcr.io/rybbit-io/rybbit-client:${IMAGE_TAG:-latest}
    container_name: client
    build:
      context: .
      dockerfile: ./client/Dockerfile
      args:
        NEXT_PUBLIC_BACKEND_URL: ${BASE_URL}
        NEXT_PUBLIC_DISABLE_SIGNUP: ${DISABLE_SIGNUP}
        NEXT_PUBLIC_DEPLOYMENT: ${DEPLOYMENT}
        NEXT_PUBLIC_LITE_DASHBOARD: ${LITE_DASHBOARD}
    ports:
      - "${HOST_CLIENT_PORT:-127.0.0.1:3002:3002}"
    environment:
      - NODE_ENV=production
      - NEXT_PUBLIC_BACKEND_URL=${BASE_URL}
      - NEXT_PUBLIC_DISABLE_SIGNUP=${DISABLE_SIGNUP}
      - NEXT_PUBLIC_DEPLOYMENT=${DEPLOYMENT}
      - NEXT_PUBLIC_LITE_DASHBOARD=${LITE_DASHBOARD}
    depends_on:
      - backend
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"

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: |
      <clickhouse>
          <listen_host>0.0.0.0</listen_host>
      </clickhouse>

  clickhouse_logging:
    content: |
      <clickhouse>
        <logger>
            <level>warning</level>
            <console>true</console>
        </logger>
        <query_thread_log remove="remove"/>
        <query_log remove="remove"/>
        <query_views_log remove="remove"/>
        <query_metric_log remove="remove"/>
        <error_log remove="remove"/>
        <opentelemetry_span_log remove="remove"/>
        <text_log remove="remove"/>
        <trace_log remove="remove"/>
        <metric_log remove="remove"/>
        <asynchronous_metric_log remove="remove"/>
        <session_log remove="remove"/>
        <part_log remove="remove"/>
        <latency_log remove="remove"/>
        <processors_profile_log remove="remove"/>
      </clickhouse>

  clickhouse_resource_limits:
    content: |
      <clickhouse>
        <!-- Leave headroom for the backend, Redis, Caddy, the kernel, and the
             filesystem cache instead of allowing ClickHouse to exhaust RAM. -->
        <max_server_memory_usage_to_ram_ratio>0.80</max_server_memory_usage_to_ram_ratio>
        <!-- Share at most one query-processing thread per CPU core across all
             concurrent queries. This throttles work rather than rejecting it. -->
        <concurrent_threads_soft_limit_ratio_to_cores>1</concurrent_threads_soft_limit_ratio_to_cores>
        <!-- Bound memory used by background merges and mutations separately. -->
        <merges_mutations_memory_usage_to_ram_ratio>0.25</merges_mutations_memory_usage_to_ram_ratio>
      </clickhouse>

  clickhouse_user_settings:
    content: |
      <clickhouse>
        <profiles>
          <default>
            <enable_json_type>1</enable_json_type>
            <!-- Combine concurrent inserts into larger blocks inside ClickHouse.
                 Wait for the flush so the backend still receives insert errors. -->
            <async_insert>1</async_insert>
            <wait_for_async_insert>1</wait_for_async_insert>
            <log_queries>0</log_queries>
            <log_query_threads>0</log_query_threads>
            <log_processors_profiles>0</log_processors_profiles>
            <!-- Bound individual queries while preserving enough parallelism
                 for large analytics scans. Query-count limits remain unset so
                 ingestion is never rejected by admission control. -->
            <max_memory_usage>32000000000</max_memory_usage>
            <max_threads>16</max_threads>
          </default>
        </profiles>
      </clickhouse>