Open source · your infrastructure

Self-host TraceIQ

Run a private local instance with one command, or deploy the API, PostgreSQL, Redis, and dashboard on infrastructure you control. Local authentication needs no hosted identity provider; Supabase remains available for shared installations that need accounts, teams, invitations, and 2FA.

One-command private installation

Install Docker with Docker Compose, clone the repository, and start the complete local stack:

bash
docker compose -f compose.local.yml up -d --build

Open http://localhost:3000. Compose starts PostgreSQL 18, runs every pending migration, starts Redis and the API, then starts the dashboard. No TraceIQ account or Supabase project is required.

Project IDproj_localThe project created automatically for local mode.
Write keytrq_local_development_onlyDevelopment key for browser, mobile, server, and HTTP ingestion.
API URLhttp://localhost:4000Loopback-bound API endpoint used by the SDK and dashboard.
bash
docker compose -f compose.local.yml ps
curl http://localhost:4000/health
Local mode is designed for one operator on their own machine. Both HTTP services bind to loopback, and PostgreSQL and Redis are not published. Do not expose this configuration through a public reverse proxy.

Stop the containers without deleting PostgreSQL data:

bash
docker compose -f compose.local.yml down

Choose an authentication mode

Supabase is not required. Choose the path that matches who can reach the dashboard:

No hosted auth

Private local mode

No login, Supabase project, or external identity service. Intended for one operator on localhost, a private machine, or a trusted private network.

AUTH_MODE=local
NEXT_PUBLIC_AUTH_MODE=local
Team authentication

Supabase mode

Use when multiple people need accounts, organizations, invitations, team permissions, and two-factor authentication.

AUTH_MODE=supabase
NEXT_PUBLIC_AUTH_MODE=supabase

The API and dashboard modes must match. Supabase variables are read only in Supabase mode. Local mode contains no login boundary, so never expose its dashboard directly to the public internet; keep it loopback-bound, behind a VPN, or protected by a trusted access proxy.

Production topology

The verified production shape keeps the public dashboard and API deployment independent:

text
Dashboard host (Vercel or Node.js)
              |
              v
API domain -> Caddy -> 127.0.0.1:4000
                         |
                    Docker Compose
                    |- NestJS API
                    |- PostgreSQL 18
                    - Redis 7
  • Dashboard — Deploy apps/dashboard as a Next.js application and set its NEXT_PUBLIC_* values at build time.
  • API — Bind port 4000 to loopback and expose it only through a TLS reverse proxy such as Caddy.
  • PostgreSQL and Redis — Keep both services on the private Compose network; do not publish their ports.
  • CORS — Set CORS_ORIGINS to the exact dashboard and instrumented website origins that should reach the API.

Production environment

Copy .env.example to .env, replace every placeholder, and keep the file out of Git. Begin with the values common to both authentication modes:

dotenv
DATABASE_URL=postgresql://traceiq:UNIQUE_PASSWORD@postgres:5432/traceiq?sslmode=disable
DATABASE_SSL=false
REDIS_URL=redis://redis:6379
CORS_ORIGINS=https://analytics.example.com,https://www.example.com
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_WAITLIST_ONLY=false

Option A: private local authentication

dotenv
AUTH_MODE=local
NEXT_PUBLIC_AUTH_MODE=local
LOCAL_PROJECT_WRITE_KEY=replace-with-a-random-write-key

# Required only when the API container binds beyond loopback internally.
# Keep the host port loopback-only or privately protected.
LOCAL_AUTH_ALLOW_NETWORK=true

No SUPABASE_* or NEXT_PUBLIC_SUPABASE_* values are needed for this path.

Option B: Supabase authentication

dotenv
AUTH_MODE=supabase
NEXT_PUBLIC_AUTH_MODE=supabase

SUPABASE_URL=https://YOUR_PROJECT.supabase.co
SUPABASE_ANON_KEY=YOUR_ANON_KEY
SUPABASE_SERVICE_ROLE_KEY=YOUR_SERVICE_ROLE_KEY

NEXT_PUBLIC_SUPABASE_URL=https://YOUR_PROJECT.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_ANON_KEY

Inside Compose, use postgres rather than localhost as the database hostname. Only Supabase-mode installations need to configure the production dashboard callback URL in Supabase Auth. Never expose SUPABASE_SERVICE_ROLE_KEY to the dashboard or browser.

First VPS deployment

bash
docker compose up -d postgres redis
docker compose build api
docker compose run --rm api npm run migrate --workspace=@traceiq/api
docker compose up -d api
docker compose ps
curl http://127.0.0.1:4000/health

Point the API DNS record to the VPS and proxy the public API hostname to the loopback API:

caddy
api.example.com {
  reverse_proxy 127.0.0.1:4000
}

Then build or deploy the dashboard with its production environment values and verify that its origin is included in CORS_ORIGINS.

Backups, migrations, and safe upgrades

Back up PostgreSQL before migrations or major upgrades. Store the dump outside the Compose volume and test restoration periodically.

bash
docker compose exec -T postgres pg_dump -U traceiq -d traceiq --no-owner --no-acl > traceiq-backup.sql
bash
git pull --ff-only
docker compose build api
docker compose run --rm api npm run migrate --workspace=@traceiq/api
docker compose up -d api
docker compose ps
curl http://127.0.0.1:4000/health
Never use docker compose down -v during an upgrade. The -v flag removes the PostgreSQL volume and can permanently delete analytics data.

Operational checklist

  • Credentials — Use unique database passwords and rotate exposed Supabase, project, email, AI, and access-token secrets.
  • Backups — Back up before migrations, keep copies outside the VPS, and test restoration.
  • Network — Expose only the TLS proxy; keep API, PostgreSQL, and Redis ports private or loopback-bound.
  • Monitoring — Watch API health, disk capacity, PostgreSQL growth, backup completion, and container restarts.
  • Updates — Review dependency and container-image changes before deploying them.