← Back to Blog

Self-Hosting Traceflair with Docker: A Complete Guide

Self-hosting your analytics gives you something no SaaS platform can: complete control over your data. Every page view, session recording, and heatmap stays on your servers. No third-party data processors, no cross-border data transfers, and no vendor who might change their privacy policy tomorrow. This guide walks you through deploying Traceflair on your own infrastructure using Docker Compose.

Why Self-Host Your Analytics

Data Sovereignty

When analytics data lives on a third-party service, it is subject to that service's jurisdiction, policies, and security practices. Self-hosting means your data is governed by your policies, stored in your chosen jurisdiction, and accessible only to your team. For organizations in regulated industries like healthcare, finance, or government, self-hosting is often not a preference but a requirement.

Cost Control

SaaS analytics pricing scales with traffic. A site with 10 million monthly page views might pay hundreds or thousands of dollars per month for commercial analytics with session replay. Self-hosting Traceflair on a server costing $20-50 per month handles the same volume. The cost does not increase with traffic; it increases only if you need more server resources.

Compliance Simplification

GDPR requires a legal basis for transferring personal data to third parties. When you self-host, there is no third party. You are both the data controller and the processor. This eliminates the need for Data Processing Agreements, Standard Contractual Clauses, and Transfer Impact Assessments with your analytics vendor. Read more about Traceflair's privacy-first approach to see how this works in practice.

Prerequisites

Before starting, ensure you have the following:

  • Docker Engine 20.10+ and Docker Compose v2 installed on your server. Both are included in Docker Desktop for development, or install Docker Engine directly on Linux servers.
  • A server or VPS with at least 2GB RAM and 2 CPU cores. For production workloads with session replay, 4GB RAM is recommended. Any major cloud provider (AWS, Hetzner, DigitalOcean, Linode) or an on-premise server works.
  • A domain name pointed at your server's IP address, with ports 80 and 443 accessible. You will need this for HTTPS, which is required for the tracking script to work on HTTPS websites.
  • Basic command-line familiarity. This guide uses standard Linux commands.

You do not need to install PostgreSQL or Redis separately. Both are included in the Docker Compose configuration.

Step 1: Create the Project Directory

SSH into your server and create a directory for Traceflair:

mkdir -p /opt/traceflair
cd /opt/traceflair

Step 2: Create the Environment File

Create a .env file with your configuration. This file contains sensitive values, so set appropriate file permissions:

# /opt/traceflair/.env

# Required: a random string used to sign JWT tokens.
# Generate one with: openssl rand -hex 32
JWT_SECRET=your-random-secret-here

# Database configuration (used by both PostgreSQL and the server)
POSTGRES_USER=traceflair
POSTGRES_PASSWORD=a-strong-database-password
POSTGRES_DB=traceflair
DATABASE_URL=postgresql://traceflair:a-strong-database-password@postgres:5432/traceflair

# Redis (used for session caching and rate limiting)
REDIS_URL=redis://redis:6379

# Server configuration
NODE_ENV=production
PORT=3001

# Optional: Stripe billing (leave empty to disable)
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=

# Optional: PayPal billing (leave empty to disable)
PAYPAL_CLIENT_ID=
PAYPAL_CLIENT_SECRET=

# Optional: Email via Resend (leave empty for console output)
RESEND_API_KEY=

Set restrictive permissions on the environment file:

chmod 600 /opt/traceflair/.env

Step 3: Create the Docker Compose File

Create the docker-compose.yml file that defines the entire stack:

# /opt/traceflair/docker-compose.yml
version: "3.8"

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - redisdata:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  server:
    image: traceflair/server:latest
    restart: unless-stopped
    ports:
      - "3001:3001"
    env_file:
      - .env
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  dashboard:
    image: traceflair/dashboard:latest
    restart: unless-stopped
    ports:
      - "3000:3000"
    depends_on:
      - server

volumes:
  pgdata:
  redisdata:

Step 4: Start the Stack

Launch all services with a single command:

docker compose up -d

Docker will pull the images (this takes a minute or two on first run), start PostgreSQL and Redis, wait for them to be healthy, and then start the Traceflair server and dashboard. Watch the logs to confirm everything starts correctly:

docker compose logs -f server

You should see output indicating the server has connected to PostgreSQL and Redis, run database migrations, and started listening on port 3001. Press Ctrl+C to stop following the logs.

Step 5: Set Up a Reverse Proxy with HTTPS

The Traceflair server and dashboard run on ports 3001 and 3000, respectively. For production use, you need a reverse proxy that handles HTTPS termination. Here is a minimal nginx configuration:

# /etc/nginx/sites-available/traceflair
server {
    listen 443 ssl http2;
    server_name analytics.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/analytics.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/analytics.yourdomain.com/privkey.pem;

    # Dashboard
    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }

    # API server
    location /v1/ {
        proxy_pass http://127.0.0.1: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;
    }

    # API auth and other server routes
    location /api/ {
        proxy_pass http://127.0.0.1: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;
    }
}

Use Certbot to obtain a free Let's Encrypt certificate:

sudo certbot --nginx -d analytics.yourdomain.com

Step 6: Verify the Installation

With the stack running and the reverse proxy configured, verify each component:

Check the API health endpoint:

curl https://analytics.yourdomain.com/v1/health
# Expected: {"status":"ok"}

Access the dashboard: Open https://analytics.yourdomain.com in your browser. You should see the Traceflair login screen. Create your first admin account through the setup wizard.

Add the tracking script to your website: After creating a site in the dashboard, add the Traceflair script to your pages:

<script
  src="https://analytics.yourdomain.com/v1/script.js"
  data-site-id="your-site-id"
  data-consent="opt-in">
</script>

Verify events are being received: Visit your website in a browser, then check the Traceflair dashboard. You should see the page view appear within a few seconds.

Environment Variables Reference

Here is a complete reference of all environment variables the server accepts:

  • DATABASE_URL (required) — PostgreSQL connection string.
  • JWT_SECRET (required) — Random string for signing authentication tokens. Use at least 32 characters.
  • REDIS_URL (optional) — Redis connection string. Defaults to redis://localhost:6379. Used for session caching and rate limiting.
  • PORT (optional) — Server port. Defaults to 3001.
  • NODE_ENV (optional) — Set to production for production deployments. Enables security headers and disables debug logging.
  • STRIPE_SECRET_KEY (optional) — Stripe API key for billing. Leave empty to disable Stripe billing.
  • STRIPE_WEBHOOK_SECRET (optional) — Stripe webhook signing secret.
  • PAYPAL_CLIENT_ID (optional) — PayPal client ID. Leave empty to disable PayPal billing.
  • PAYPAL_CLIENT_SECRET (optional) — PayPal client secret.
  • RESEND_API_KEY (optional) — Resend API key for transactional emails. When not set, emails are logged to the console.

Updating Traceflair

To update to the latest version, pull the new images and restart:

cd /opt/traceflair
docker compose pull
docker compose up -d

The server automatically runs database migrations on startup, so schema updates are applied without manual intervention. It is good practice to back up your PostgreSQL data before updating:

docker compose exec postgres pg_dump -U traceflair traceflair > backup-$(date +%Y%m%d).sql

Backing Up Your Data

Your analytics data is stored in two places: PostgreSQL (events, sessions, user data) and Redis (ephemeral caches). Only PostgreSQL needs to be backed up. Set up a daily cron job:

# Add to crontab: crontab -e
0 3 * * * cd /opt/traceflair && docker compose exec -T postgres pg_dump -U traceflair traceflair | gzip > /opt/traceflair/backups/traceflair-$(date +\%Y\%m\%d).sql.gz

Next Steps

With Traceflair running on your infrastructure, you have a complete analytics platform under your control. Here are some next steps:

If you have questions about self-hosting or need help with your deployment, reach out to our team. For teams that prefer not to manage infrastructure, we also offer a hosted version with the same features and a generous free tier.

Your users are telling you something. Start listening.

Free to start. No credit card required. Set up in under 5 minutes.

Start Free Trial