Rybbit

Manual Docker Compose Setup

Manual setup guide for Rybbit using Docker Compose without the setup script

This guide is for users who want to manually set up Rybbit using Docker Compose without the setup script. This is useful if you already have a reverse proxy setup (like Nginx, Traefik, or Coolify), or if you want more control over the configuration.

Prerequisites

  • Docker and Docker Compose installed
  • A domain name pointed to your server
  • Basic knowledge of Docker and environment variables

Setup Steps

Clone the Repository

git clone https://github.com/rybbit-io/rybbit.git
cd rybbit

Create Environment File

Create a .env file in the root directory with the following content:

# Required: Your domain and base URL
DOMAIN_NAME=your.domain.com
BASE_URL=https://your.domain.com

# Required: Authentication secret (generate a random 32+ character string)
BETTER_AUTH_SECRET=your-very-long-random-secret-string-here

# Optional: Disable new user signups after creating admin account
DISABLE_SIGNUP=false

# Optional: Custom ports (only needed if you want different ports)
# HOST_BACKEND_PORT="3001:3001"
# HOST_CLIENT_PORT="3002:3002"

# Optional: Database credentials (defaults work fine)
# CLICKHOUSE_PASSWORD=frog
# POSTGRES_USER=frog
# POSTGRES_PASSWORD=frog
# POSTGRES_DB=analytics
# CLICKHOUSE_DB=analytics

To generate a secure BETTER_AUTH_SECRET, you can use:

openssl rand -hex 32

Choose Your Setup Method

If you don't have a reverse proxy and want automatic SSL certificates, use the default docker-compose.yml:

docker compose up -d

This will:

  • Start all services including Caddy
  • Automatically obtain SSL certificates
  • Make your app available at https://your.domain.com

If you have your own reverse proxy (Nginx, Traefik, Coolify, etc.), you need to exclude Caddy and expose the ports:

  1. Option B1: Modify the .env file

    # Add these lines to expose ports
    HOST_BACKEND_PORT="3001:3001"
    HOST_CLIENT_PORT="3002:3002"

    Then start without Caddy:

    docker compose up -d backend client clickhouse postgres
  2. Option B2: Use docker-compose.override.yml Create a docker-compose.override.yml file:

    services:
      backend:
        ports:
          - "3001:3001"
      client:
        ports:
          - "3002:3002"

    Then start without Caddy:

    docker compose up -d backend client clickhouse postgres

Configure Your Reverse Proxy

If you're using your own reverse proxy, configure it to:

  • Proxy requests to /api/* to http://localhost:3001
  • Proxy all other requests to http://localhost:3002

Example configurations:

server {
    listen 80;
    server_name your.domain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name your.domain.com;

    # Your SSL configuration here

    location /api/ {
        proxy_pass http://localhost:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location / {
        proxy_pass http://localhost:3002;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
services:
  backend:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.rybbit-api.rule=Host(`your.domain.com`) && PathPrefix(`/api`)"
      - "traefik.http.routers.rybbit-api.tls.certresolver=letsencrypt"
      - "traefik.http.services.rybbit-api.loadbalancer.server.port=3001"
  
  client:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.rybbit-client.rule=Host(`your.domain.com`)"
      - "traefik.http.routers.rybbit-client.tls.certresolver=letsencrypt"
      - "traefik.http.services.rybbit-client.loadbalancer.server.port=3002"

Start the Services

# Start all services
docker compose up -d

# Or start specific services (without Caddy)
docker compose up -d backend client clickhouse postgres

Verify Setup

Check that all services are running:

docker compose ps

Monitor logs:

docker compose logs -f

Create Admin Account

Navigate to https://your.domain.com/signup and create your admin account.

Service Architecture

Rybbit consists of these services:

  • client: Next.js frontend (port 3002)
  • backend: Node.js API server (port 3001)
  • postgres: User data and configuration
  • clickhouse: Analytics data storage
  • caddy: Reverse proxy with automatic SSL (optional)

Common Configurations

Using Custom Ports

If you need different host ports (e.g., if 3001/3002 are already in use):

HOST_BACKEND_PORT="8080:3001"
HOST_CLIENT_PORT="8081:3002"

Using with Coolify

Since Coolify uses Traefik instead of Caddy as proxy, some modifications have to be made in a docker-compose.yaml file to avoid conflicts. Specifically, you need to:

  • remove Caddy service and volumes
  • add network labels to all services
  • remove build and ports properties from backend and client services

Here's a modified version of docker-compose that works on Coolify:

services:
  clickhouse:
    container_name: clickhouse
    image: clickhouse/clickhouse-server:25.4.2
    volumes:
      - clickhouse-data:/var/lib/clickhouse
    configs:
      - source: clickhouse_network
        target: /etc/clickhouse-server/config.d/network.xml
      - source: clickhouse_json
        target: /etc/clickhouse-server/config.d/enable_json.xml
      - source: clickhouse_logging
        target: /etc/clickhouse-server/config.d/logging_rules.xml
      - source: clickhouse_user_logging
        target: /etc/clickhouse-server/config.d/user_logging.xml
    environment:
      - CLICKHOUSE_DB=${CLICKHOUSE_DB:-analytics}
      - CLICKHOUSE_USER=${CLICKHOUSE_USER:-default}
      - CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD:-frog}
    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
    labels:
      - traefik.enable=false

  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
    labels:
      - traefik.enable=false

  backend:
    image: ghcr.io/rybbit-io/rybbit-backend:${IMAGE_TAG:-latest}
    container_name: backend
    environment:
      - NODE_ENV=production
      - CLICKHOUSE_HOST=http://clickhouse:8123
      - CLICKHOUSE_DB=${CLICKHOUSE_DB:-analytics}
      - CLICKHOUSE_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}
      - DOMAIN_NAME=${DOMAIN_NAME}
      - DISABLE_SIGNUP=${DISABLE_SIGNUP}
      - DISABLE_TELEMETRY=${DISABLE_TELEMETRY}
    depends_on:
      clickhouse:
        condition: service_healthy
      postgres:
        condition: service_started
    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
    labels:
      - traefik.enable=true
      - traefik.docker.network=coolify
      - traefik.http.routers.rybbit-backend.entrypoints=https
      - traefik.http.routers.rybbit-backend.tls=true
      - traefik.http.routers.rybbit-backend.rule=Host(`${DOMAIN_NAME}`) && (Path(`/api`) || PathPrefix(`/api/`))
      - traefik.http.services.rybbit-backend.loadbalancer.server.port=3001
      - traefik.http.routers.rybbit-backend.priority=100
      - traefik.http.middlewares.rybbit-forward-headers.headers.customrequestheaders.X-Forwarded-Proto=https
      - traefik.http.middlewares.rybbit-forward-headers.headers.customrequestheaders.X-Forwarded-Host=${DOMAIN_NAME}
      - traefik.http.routers.rybbit-backend.middlewares=rybbit-forward-headers

  client:
    image: ghcr.io/rybbit-io/rybbit-client:${IMAGE_TAG:-latest}
    container_name: client
    environment:
      - NODE_ENV=production
      - NEXT_PUBLIC_BACKEND_URL=${BASE_URL}
      - NEXT_PUBLIC_DISABLE_SIGNUP=${DISABLE_SIGNUP}
      - DOMAIN_NAME=${DOMAIN_NAME}
    depends_on:
      - backend
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.docker.network=coolify
      - traefik.http.routers.rybbit-client.entrypoints=https
      - traefik.http.routers.rybbit-client.tls=true
      - traefik.http.routers.rybbit-client.rule=Host(`${DOMAIN_NAME}`)
      - traefik.http.services.rybbit-client.loadbalancer.server.port=3002
      - traefik.http.routers.rybbit-client.priority=10

volumes:
  clickhouse-data:
  postgres-data:
  redis-data:

configs:
  clickhouse_network:
    content: |
      <clickhouse>
          <listen_host>0.0.0.0</listen_host>
      </clickhouse>

  clickhouse_json:
    content: |
      <clickhouse>
          <settings>
              <enable_json_type>1</enable_json_type>
          </settings>
      </clickhouse>

  clickhouse_logging:
    content: |
      <clickhouse>
        <logger>
            <level>warning</level>
            <console>true</console>
        </logger>
        <query_thread_log remove="remove"/>
        <query_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_user_logging:
    content: |
      <clickhouse>
        <profiles>
          <default>
            <log_queries>0</log_queries>
            <log_query_threads>0</log_query_threads>
            <log_processors_profiles>0</log_processors_profiles>
          </default>
        </profiles>
      </clickhouse>

To deploy Rybbit on Coolify, you need to:

  1. Create a new project and resource using docker-compose.yaml file provided above
  2. Make sure "Escape special characters in labels" option is unchecked when adding docker-compose file so that env variables in labels are read properly
  3. Set the environment variables in Coolify's interface
  4. Add a domain for client service in Coolify's interface
  5. Deploy all services

Note: you don't need to add a domain for backend service in Coolify since client and backend services share the same domain.

Using with Nginx Proxy Manager

  1. Set up port exposure in your .env:

    HOST_BACKEND_PORT="3001:3001"
    HOST_CLIENT_PORT="3002:3002"
  2. Create a proxy host pointing to your server IP:3002

  3. Add a custom location /api/* pointing to your server IP:3001

Database Persistence

Data is automatically persisted in Docker volumes:

  • rybbit_clickhouse-data: Analytics data
  • rybbit_postgres-data: User accounts and site configurations

Advanced Configurations

For specific reverse proxy setups, see our detailed guides:

These guides provide step-by-step instructions for integrating Rybbit with your existing infrastructure.

Troubleshooting

Services won't start

# Check logs
docker compose logs

# Check service status
docker compose ps

Port conflicts

If you get port binding errors, either:

  1. Change the host ports in your .env file
  2. Stop the conflicting service
  3. Use docker-compose.override.yml to customize ports

Database connection issues

Ensure the database services are healthy:

docker compose ps

Both postgres and clickhouse should show "healthy" status.

For more advanced configurations and setup script options, see our Advanced Self-Hosting Guide.