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-tubeCreate 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=analyticsTo generate a secure BETTER_AUTH_SECRET, you can use:
openssl rand -hex 32Choose 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 -dThis 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:
-
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 -
Option B2: Use docker-compose.override.yml Create a
docker-compose.override.ymlfile: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/*tohttp://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 postgresVerify Setup
Check that all services are running:
docker compose psMonitor logs:
docker compose logs -fCreate 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
buildandportsproperties 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:
- Create a new project and resource using
docker-compose.yamlfile provided above - Make sure "Escape special characters in labels" option is unchecked when adding docker-compose file so that env variables in labels are read properly
- Set the environment variables in Coolify's interface
- Add a domain for
clientservice in Coolify's interface - 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
-
Set up port exposure in your .env:
HOST_BACKEND_PORT="3001:3001" HOST_CLIENT_PORT="3002:3002" -
Create a proxy host pointing to your server IP:3002
-
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 dataanalytics-tube_postgres-data: User accounts and site configurations
Advanced Configurations
For specific reverse proxy setups, see our detailed guides:
- Custom Nginx Setup - Complete Nginx configuration with SSL
- Nginx Proxy Manager - NPM setup without port exposure
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 psPort conflicts
If you get port binding errors, either:
- Change the host ports in your .env file
- Stop the conflicting service
- Use docker-compose.override.yml to customize ports
Database connection issues
Ensure the database services are healthy:
docker compose psBoth postgres and clickhouse should show "healthy" status.
For more advanced configurations and setup script options, see our Advanced Self-Hosting Guide.