Deploy TRACIO on your own infrastructure with Docker. Full data ownership, GDPR-compliant by design, with horizontal scaling support.
TRACIO can be self-hosted on your own infrastructure. This gives you full ownership of visitor data, eliminates third-party dependencies, and ensures GDPR compliance by design. All data stays within your infrastructure boundary.
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4+ cores |
| RAM | 2 GB | 8+ GB |
| Disk | 20 GB | 100+ GB (for ClickHouse analytics) |
| OS | Linux (amd64 or arm64) | Ubuntu 22.04+ or Debian 12+ |
| Docker | 24.0+ | Latest stable |
| Docker Compose | 2.20+ | Latest stable |
| Service | Purpose | Required |
|---|---|---|
| Redis | Visitor cache, velocity tracking, rate limiting | Yes |
| PostgreSQL | Persistent visitor storage, API keys, webhook config | Yes |
| ClickHouse | Analytics, historical queries | Optional |
| MaxMind GeoIP2 | IP geolocation databases | Yes (free tier available) |
git clone https://github.com/tracio-ai/tracio.gitcd tracioCreate a .env file with your configuration:
# RequiredTRACIO_API_KEY=your-api-key-hereTRACIO_API_SECRET=your-api-secret-here
# RedisTRACIO_REDIS_URL=redis://redis:6379/0
# PostgreSQLTRACIO_POSTGRES_URL=postgres://tracio:tracio_password@postgres:5432/tracio?sslmode=disable
# ClickHouse (optional)TRACIO_CLICKHOUSE_URL=clickhouse://clickhouse:9000/tracio
# MaxMind GeoIP2TRACIO_GEOIP_CITY_DB=/data/geoip/GeoLite2-City.mmdbTRACIO_GEOIP_ISP_DB=/data/geoip/GeoLite2-ASN.mmdbTRACIO_MAXMIND_LICENSE_KEY=your-maxmind-key
# TURN Server (for WebRTC IP probe)TRACIO_TURN_ENABLED=trueTRACIO_TURN_PUBLIC_IP=your-server-public-ipTRACIO_TURN_PORT=3478TRACIO_TURN_SECRET=admin
# ServerTRACIO_PORT=8080TRACIO_HOST=0.0.0.0TRACIO_LOG_LEVEL=infodocker compose up -dThis starts:
# Health checkcurl http://localhost:8080/health
# Test identification (from a browser)open http://localhost:8080/demo┌─────────────────────────────────────────────────────────────┐│ Your Infrastructure ││ ││ ┌──────────┐ ┌──────────────┐ ┌───────────────┐ ││ │ Nginx │───▶│ TRACIO │───▶│ Redis │ ││ │ (TLS/LB) │ │ Ingress │ │ (visitor │ ││ └──────────┘ │ (Go server) │ │ cache) │ ││ └──────────────┘ └───────────────┘ ││ │ ││ ├───────────▶ PostgreSQL ││ ├───────────▶ ClickHouse ││ ├───────────▶ MaxMind GeoIP ││ └───────────▶ TURN Server │└─────────────────────────────────────────────────────────────┘Place Nginx in front of the TRACIO server for TLS termination and load balancing:
server { listen 443 ssl http2; server_name api.tracio.ai;
ssl_certificate /etc/ssl/certs/api.tracio.ai.pem; ssl_certificate_key /etc/ssl/private/api.tracio.ai.key;
# TLS configuration for JA4 fingerprinting ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off;
# Forward TLS fingerprint headers proxy_set_header X-TLS-Version $ssl_protocol; proxy_set_header X-TLS-Cipher $ssl_cipher;
location / { proxy_pass http://127.0.0.1:8080; 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;
add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Credentials true always; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always; add_header Access-Control-Allow-Headers "Content-Type" always;
if ($request_method = OPTIONS) { return 204; } }
location /tracio-client.js { proxy_pass http://127.0.0.1:8080/tracio-client.js; proxy_cache_valid 200 1h; add_header Cache-Control "public, max-age=3600"; }}The TRACIO ingress server is stateless. Scale horizontally by running multiple instances behind a load balancer:
# docker-compose.prod.ymlservices: tracio-ingress: image: tracio/ingress:latest deploy: replicas: 4 resources: limits: cpus: '2' memory: 2GRedis and PostgreSQL handle shared state. Ensure they are provisioned for the expected request volume.
| Metric | Per Instance | Notes |
|---|---|---|
| Requests/second | 500-1000 | Depends on CPU and signal complexity |
| Memory per request | ~2 KB | Signal data + processing buffers |
| Redis operations/request | 3-5 | Visitor cache + velocity + rate limit |
| PostgreSQL writes/request | 1 | Visitor record insert/update |
| ClickHouse writes/request | 1 | Analytics event (batched) |
| Traffic | Redis Memory | Notes |
|---|---|---|
| 10K visitors/day | 50 MB | Velocity data + cache |
| 100K visitors/day | 500 MB | Consider Redis Cluster |
| 1M visitors/day | 5 GB | Redis Cluster recommended |
TRACIO requires MaxMind GeoIP2 databases for IP geolocation and ASN lookup.
# Using maxmind's geoipupdate tooldocker run --rm -v /data/geoip:/usr/share/GeoIP \ -e GEOIPUPDATE_ACCOUNT_ID=YOUR_ACCOUNT_ID \ -e GEOIPUPDATE_LICENSE_KEY=YOUR_LICENSE_KEY \ -e GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN" \ maxmindinc/geoipupdateSchedule weekly database updates:
# crontab0 3 * * 3 docker run --rm -v /data/geoip:/usr/share/GeoIP \ -e GEOIPUPDATE_ACCOUNT_ID=YOUR_ACCOUNT_ID \ -e GEOIPUPDATE_LICENSE_KEY=YOUR_LICENSE_KEY \ -e GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN" \ maxmindinc/geoipupdate && docker restart tracio-ingressThe TURN server enables WebRTC IP probing (s94 signal) for VPN detection. It requires direct TCP/UDP access on port 3478 and must not be behind a CDN like Cloudflare.
TRACIO_TURN_ENABLED=trueTRACIO_TURN_PUBLIC_IP=203.0.113.10 # Your server's public IPTRACIO_TURN_PORT=3478 # TURN listening portTRACIO_TURN_SECRET=admin # Shared credentialEnsure port 3478 (TCP and UDP) is open in your firewall.
GET /health# Returns 200 OK with JSON:# { "status": "ok", "version": "2.0.0", "uptime": "48h32m" }The admin dashboard (port 8081) provides real-time metrics:
{ "level": "info", "ts": "2024-03-12T16:00:00.000Z", "msg": "identification", "visitor_id": "X7fh2Hg9LkMn3pQr", "confidence": 0.995, "bot": "notDetected", "latency_ms": 12, "ip": "94.142.239.124"}Last updated: April 2026