analytics.tube
Self-Hosting Guides

Manual Docker Compose setup

Manual setup guide for analytics-tube using Docker Compose without the setup script

This guide is for users who want to manually set up analytics-tube 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/tavoweb/analytics.tube.git
cd analytics-tube

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 but recommended: Mapbox token for globe visualizations
MAPBOX_TOKEN=your_mapbox_token

# 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.analytics-tube-api.rule=Host(`your.domain.com`) && PathPrefix(`/api`)"
      - "traefik.http.routers.analytics-tube-api.tls.certresolver=letsencrypt"
      - "traefik.http.services.analytics-tube-api.loadbalancer.server.port=3001"
  
  client:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.analytics-tube-client.rule=Host(`your.domain.com`)"
      - "traefik.http.routers.analytics-tube-client.tls.certresolver=letsencrypt"
      - "traefik.http.services.analytics-tube-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

analytics-tube 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/tavoweb/analytics.tube-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}
      - MAPBOX_TOKEN=${MAPBOX_TOKEN}
    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.analytics-tube-backend.entrypoints=https
      - traefik.http.routers.analytics-tube-backend.tls=true
      - traefik.http.routers.analytics-tube-backend.rule=Host(`${DOMAIN_NAME}`) && (Path(`/api`) || PathPrefix(`/api/`))
      - traefik.http.services.analytics-tube-backend.loadbalancer.server.port=3001
      - traefik.http.routers.analytics-tube-backend.priority=100
      - traefik.http.middlewares.analytics-tube-forward-headers.headers.customrequestheaders.X-Forwarded-Proto=https
      - traefik.http.middlewares.analytics-tube-forward-headers.headers.customrequestheaders.X-Forwarded-Host=${DOMAIN_NAME}
      - traefik.http.routers.analytics-tube-backend.middlewares=analytics-tube-forward-headers

  client:
    image: ghcr.io/tavoweb/analytics.tube-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.analytics-tube-client.entrypoints=https
      - traefik.http.routers.analytics-tube-client.tls=true
      - traefik.http.routers.analytics-tube-client.rule=Host(`${DOMAIN_NAME}`)
      - traefik.http.services.analytics-tube-client.loadbalancer.server.port=3002
      - traefik.http.routers.analytics-tube-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 analytics-tube 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:

  • analytics-tube_clickhouse-data: Analytics data
  • analytics-tube_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 analytics-tube 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.