Haloy
Deploy Traceway to your own server using Haloy (opens in a new tab), a CLI-based deployment tool. Haloy manages your containers, handles automatic SSL certificates, and is configured with a single YAML file.
Prerequisites
Install the Haloy CLI and configure a server by following the instructions at haloy.dev/docs (opens in a new tab).
Configuration
Create a haloy.yaml in the traceway project root. Replace haloy.tracewayapp.com with your configured Haloy server and traceway.yourdomain.com with the domain you want Traceway accessible at.
# haloy api endpoint
server: haloy.tracewayapp.com
targets:
clickhouse:
preset: database
image:
repository: clickhouse/clickhouse-server:24.8-alpine
port: 9000
env:
- name: CLICKHOUSE_DB
value: traceway
- name: CLICKHOUSE_PASSWORD
from:
env: CLICKHOUSE_PASSWORD
volumes:
- clickhouse-data:/var/lib/clickhouse
postgres:
preset: database
image:
repository: postgres:17
port: 5432
env:
- name: POSTGRES_USER
value: traceway
- name: POSTGRES_PASSWORD
from:
env: POSTGRES_PASSWORD # from .env
- name: POSTGRES_DB
value: traceway
volumes:
- postgres-data:/var/lib/postgresql/data
traceway:
preset: service
image:
repository: traceway/traceway:minimal
build_config:
dockerfile: ./Dockerfile.minimal
domains:
- domain: traceway.yourdomain.com # will automatically set up TLS
port: 80
env:
- name: APP_BASE_URL
value: https://traceway.yourdomain.com # match domain
- name: CLICKHOUSE_SERVER
value: "clickhouse:9000"
- name: CLICKHOUSE_DATABASE
value: traceway
- name: CLICKHOUSE_USERNAME
value: default
- name: CLICKHOUSE_PASSWORD
from:
env: CLICKHOUSE_PASSWORD
- name: CLICKHOUSE_TLS
value: "false"
- name: POSTGRES_HOST
value: postgres
- name: POSTGRES_PORT
value: "5432"
- name: POSTGRES_DATABASE
value: traceway
- name: POSTGRES_USERNAME
value: traceway
- name: POSTGRES_PASSWORD
from:
env: POSTGRES_PASSWORD
- name: POSTGRES_SSLMODE
value: disable
- name: JWT_SECRET
from:
env: JWT_SECRET
# Optional: SSO providers. Omit a pair to disable that button.
# See /learn/sso for the full setup walkthrough.
- name: GOOGLE_CLIENT_ID
from:
env: GOOGLE_CLIENT_ID
- name: GOOGLE_CLIENT_SECRET
from:
env: GOOGLE_CLIENT_SECRET
- name: GITHUB_CLIENT_ID
from:
env: GITHUB_CLIENT_ID
- name: GITHUB_CLIENT_SECRET
from:
env: GITHUB_CLIENT_SECRETSensitive values use from: env: to read from environment variables on your machine at deploy time rather than being stored in the YAML file. Set them before deploying:
export CLICKHOUSE_PASSWORD=your-clickhouse-password
export POSTGRES_PASSWORD=your-postgres-password
export JWT_SECRET=your-jwt-secret-at-least-32-chars
# Optional, only when enabling SSO
export GOOGLE_CLIENT_ID=...
export GOOGLE_CLIENT_SECRET=...
export GITHUB_CLIENT_ID=...
export GITHUB_CLIENT_SECRET=...Deployment
Deploy the database services first, then the Traceway application.
1. Deploy databases:
haloy deploy -t clickhouse,postgresWait for the databases to be ready before proceeding.
2. Deploy Traceway:
haloy deploy -t tracewayAfter deploying, open https://traceway.yourdomain.com/register to create your first account.
Connecting Your SDK
Point your SDK's connection string at your Traceway domain:
<project_token>@https://traceway.yourdomain.com/api/reportEnvironment Variables
ClickHouse target
| Variable | Default | Description |
|---|---|---|
CLICKHOUSE_DB | traceway | Database to create on startup |
CLICKHOUSE_PASSWORD | (from env) | ClickHouse password |
PostgreSQL target
| Variable | Default | Description |
|---|---|---|
POSTGRES_USER | traceway | PostgreSQL username |
POSTGRES_PASSWORD | (from env) | PostgreSQL password |
POSTGRES_DB | traceway | PostgreSQL database name |
Traceway target
| Variable | Default | Description |
|---|---|---|
APP_BASE_URL | https://traceway.yourdomain.com | Public URL of the Traceway instance. Also what the deep links in notifications are built from, and the CLI/MCP OAuth issuer and device-login verification URL. See CLI Authentication. |
TRUSTED_PROXIES | (loopback) | Comma-separated CIDRs of the proxies in front of Traceway whose X-Forwarded-For / X-Real-IP is trusted for the real client IP. Every per-IP rate limit keys on it, and /api/report stores it as each session's client.ip. Unset trusts loopback only, so a proxy container on a Docker network or an in-cluster ingress has to be listed before its header counts. A value replaces the default, so list every hop between the client and this server, not just the outermost one: naming only a CDN leaves a private ingress untrusted and turns X-Forwarded-For off entirely. * trusts every peer and lets clients spoof their address. Until the proxy is listed, every visitor shares one rate-limit bucket keyed on the proxy, and the server logs a warning the first time it sees a forwarding header from a peer it does not trust. Haloy fronts the app with its own proxy, so list the range that proxy connects from (and your CDN's, if any). |
TRUSTED_PROXY_HEADER | (none) | A header taken as the client IP on every request, ahead of TRUSTED_PROXIES: X-Real-IP behind ingress-nginx, CF-Connecting-IP behind Cloudflare. Only safe when every request provably passes through a proxy that overwrites the header; a directly reachable backend lets clients set it freely. |
REPORT_MAX_BODY_MB | 64 | Cap on the decompressed /api/report and /api/profiles/ingest body in megabytes. Gzip does not raise it. A body over the cap answers 413. |
INGEST_MAX_CONCURRENT | (2 x CPU cores, min 4) | Telemetry ingest requests processed at once (/api/report, /api/profiles/ingest, /api/otel/*). Excess requests wait up to INGEST_ADMISSION_WAIT_SECONDS (default 5) and then get 503 with Retry-After; past a bounded waiting room (four times the capacity, at least 16) they are turned away immediately. Each admitted request can hold a decoded body, so on a memory-capped container size this from the memory limit rather than the CPU count. |
UPLOAD_MAX_CONCURRENT | 4 | Source map and symbol uploads processed at once. Each holds a whole file in memory while it is parsed. Excess uploads wait 30 seconds and then get 503. Separate from the ingest pool so a CI burst cannot starve telemetry. |
CLICKHOUSE_SERVER | clickhouse:9000 | ClickHouse host:port |
CLICKHOUSE_DATABASE | traceway | ClickHouse database name |
CLICKHOUSE_USERNAME | default | ClickHouse username |
CLICKHOUSE_PASSWORD | (from env) | ClickHouse password |
CLICKHOUSE_TLS | false | Enable TLS for ClickHouse |
POSTGRES_HOST | postgres | PostgreSQL host |
POSTGRES_PORT | 5432 | PostgreSQL port |
POSTGRES_DATABASE | traceway | PostgreSQL database name |
POSTGRES_USERNAME | traceway | PostgreSQL username |
POSTGRES_PASSWORD | (from env) | PostgreSQL password |
POSTGRES_SSLMODE | disable | PostgreSQL SSL mode |
JWT_SECRET | (from env) | Signs all authentication tokens (dashboard sessions and CLI/MCP device logins). Use a strong, unique secret of at least 32 chars; keep it stable. See CLI Authentication. |
GOOGLE_CLIENT_ID | (unset) | Optional. Google OAuth client ID. Setting both Google variables enables the Continue with Google button. See the SSO guide. |
GOOGLE_CLIENT_SECRET | (unset) | Optional. Google OAuth client secret. |
GITHUB_CLIENT_ID | (unset) | Optional. GitHub OAuth App client ID. Setting both GitHub variables enables the Continue with GitHub button. |
GITHUB_CLIENT_SECRET | (unset) | Optional. GitHub OAuth App client secret. |
OAUTH_SESSION_SECRET | (falls back to JWT_SECRET) | Optional. Cookie signing secret for the OAuth round-trip. |
STORAGE_TYPE | local | Blob storage backend for source maps, session recordings, and AI traces: local or s3. See Blob Storage for the S3 variables and persistence notes. |
STORAGE_PATH | ./storage | Folder for local blob storage. Point it at a persistent mount, or use STORAGE_TYPE=s3. Ignored when STORAGE_TYPE=s3. |
SESSION_RECORDING_RETENTION_DAYS | 30 | Optional. Days to keep on-disk session recordings under STORAGE_PATH/recordings/. Worker runs hourly and on startup. 0 disables; no effect when STORAGE_TYPE=s3. |
When configuring providers, set the callback URL on the provider side to https://traceway.yourdomain.com/api/auth/callback/{google|github}.
Useful Commands
# Deploy a specific target
haloy deploy -t traceway
# Check status of a specific target
haloy status -t traceway
# Check status of all targets
haloy status -a
# Validate your config
haloy validate-config