Configuration
Configure the Tindra server through environment variables. The defaults below are the binary's defaults; the installer, Docker Compose, or managed hosting may supply different values. SDK variables such as SENTRY_ENVIRONMENT belong in the application sending telemetry, not the Tindra server.
Applying changes
For Docker Compose, add runtime variables to the tindra service's environment block and recreate the container with docker compose up -d. A value in Compose's .env file only reaches the container if the service references it or uses env_file.
For a directly run binary, set the process environment or a .env file in its working directory and restart the service. Existing process variables take precedence over .env at startup.
Use the literal strings true and false, quoted in YAML. Most boolean settings enable only for the exact value true; REQUIRE_MFA is enabled unless its value is exactly false. Numeric retention, rate, and row limits accept nonnegative integers; their documented 0 behavior is specific to each setting. Buffer sizes require positive integers and fall back to defaults for invalid or nonpositive values.
The advanced SIGHUP reload path only updates PROJECT_LIMIT, EVENT_LIMIT, and USER_LIMIT after reloading a local .env file. It does not reload SSO, retention, networking, or queue settings, and cannot change an existing container's environment. Recreate the container to apply Compose changes.
Required
| Variable | Description |
|---|---|
DATABASE_URL |
Required Postgres connection string, e.g. postgres://user:password@host:5432/tindra?sslmode=disable for a private local database connection. Use your database's TLS settings for remote connections. |
PUBLIC_URL |
Set to the externally reachable instance URL, e.g. https://tindra.example.com, for correct links, DSNs, and authenticator labels. It has no binary default. |
Server
| Variable | Default | Description |
|---|---|---|
BIND_ADDR |
:8080 |
TCP address (host:port) or Unix socket path (unix:/run/tindra/tindra.sock). |
SOCKET_MODE |
0660 |
Unix socket permissions in octal. Quote values in Compose, e.g. SOCKET_MODE: "0660". Give the proxy access through matching ownership/group permissions; 0666 permits any local user to connect. |
DATA_DIR |
/data |
Persistent file storage, including source maps and attachments. Must be writable at startup. The local-development .env.example uses ./data; Docker uses /data with UID/GID 65532. |
LOG_LEVEL |
info |
Log verbosity: debug, info, warn, or error. Startup information remains visible at higher levels. |
LOG_FORMAT |
Text output | json enables JSON logs; unset or other values use text. Production Compose sets json. |
COOKIE_SECURE |
false |
Set to true when the public URL uses HTTPS, including when TLS terminates at a reverse proxy. |
CORS_ORIGIN |
Unset | Allowed CORS origin when the API and UI are served from different origins. |
Limits
Project, monthly event, user, log-row, and transaction-row caps are unlimited by default. Age retention and profile storage limits still apply.
| Variable | Default | Description |
|---|---|---|
RETENTION_DAYS |
90 |
General telemetry age retention, including events, transactions, logs, uptime checks, and completed cron check-ins. 0 disables this age cleanup only. |
PROJECT_LIMIT |
0 |
Maximum projects per instance; 0 is unlimited. |
EVENT_LIMIT |
0 |
Maximum events ingested per month; 0 is unlimited. |
USER_LIMIT |
0 |
Maximum user accounts, including new accounts admitted through SSO; 0 is unlimited. |
LOG_ROW_LIMIT |
0 |
Instance-wide log row cap, enforced by periodically deleting globally oldest entries. 0 disables the cap. |
TX_ROW_LIMIT |
0 |
Instance-wide transaction row cap, enforced by periodically deleting globally oldest transactions and their child spans. 0 disables the cap. |
Retention runs immediately at startup, then normally hourly. When a pass exhausts its deletion budget, the next catch-up pass runs after five minutes. Limits are cleanup policies, so stored rows or bytes can temporarily exceed a cap.
Row caps and profile policies run independently of RETENTION_DAYS, including when it is 0. Completed cron check-ins expire by completion time, with receipt time used for legacy records; running check-ins and monitor summaries are preserved. See Upgrades before changing retention on an existing instance.
Ingestion buffers
| Variable | Default | Description |
|---|---|---|
INGEST_BUFFER_SIZE |
10000 |
Capacity of each event, transaction, and log queue, measured in items. |
PROFILE_BUFFER_SIZE |
500 |
Profile queue capacity, measured in compressed profile items. Pending profiles also have a fixed 128 MiB byte limit. |
These queues live in memory and retry eligible failed database writes within bounded attempts and deadlines. They do not survive a process crash. Events and transactions can be rejected when their queues are full; logs and profiles are best effort and can be dropped. A successful envelope response is not a guarantee that every item has reached storage.
Keep stop_grace_period: 75s in Compose to allow HTTP shutdown and bounded queue draining. Forced termination can lose pending data. Queue size is separate from database row/storage limits.
Profiles
Profiles use their own retention window and compressed-storage budget. See Profiling.
| Variable | Default | Description |
|---|---|---|
PROFILE_RETENTION_DAYS |
7 |
Days to retain profiles. 0 disables profile age cleanup, but the storage budget still applies. |
PROFILE_STORAGE_LIMIT_MB |
2048 |
Instance-wide compressed profile storage budget in megabytes. Periodic cleanup removes oldest profiles first when exceeded. 0 disables the budget. |
To disable all profile cleanup, both profile limits must be 0. Increasing these limits does not restore previously deleted profiles.
Rate limiting
| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_LOGIN |
10 |
Maximum login attempts per IP per 15 minutes. Password-reset requests use a separate per-IP budget with the same limit. 0 disables these rate limits. |
RATE_LIMIT_ENVELOPE |
300 |
Maximum envelope POSTs per authenticated project per minute. The client key determines the project, not the URL identifier. 0 disables. |
Networking
| Variable | Default | Description |
|---|---|---|
TRUSTED_PROXIES |
Unset | Comma-separated proxy IPs/CIDRs allowed to supply client-address headers, e.g. 10.0.0.10,10.0.0.11. Trust only actual proxies; leave empty for a directly exposed instance. |
WEBHOOK_ALLOW_PRIVATE_IPS |
false |
Allow alert webhook, Slack, Discord, Microsoft Teams, and passthrough DSN requests to private/internal destinations. |
UPTIME_ALLOW_PRIVATE_IPS |
false |
Allow uptime probes to private/internal destinations. Independent of webhook policy. |
For a trusted immediate peer, Tindra walks X-Forwarded-For from right to left to the first untrusted address. A malformed supplied chain is not replaced with X-Real-IP; that fallback is considered only when no forwarded chain is supplied. Invalid entries in TRUSTED_PROXIES are skipped with a warning. See Reverse proxy.
Outbound address checks apply when connecting, including resolved addresses. Source-code enrichment always blocks internal destinations regardless of either opt-in. Uptime probes do not follow redirects; configure their expected status codes accordingly.
Security
| Variable | Default | Description |
|---|---|---|
REQUIRE_MFA |
true |
Require local MFA enrollment before normal session access, for both password and SSO users. false makes enrollment optional, but enrolled users still verify their authenticator at login. |
MFA enforcement restricts session-authenticated API access until enrollment. Project bearer tokens authenticate independently. See Authentication.
Internal
| Variable | Default | Description |
|---|---|---|
STATS_API_KEY |
Unset | Operator bearer credential for GET /api/stats and GET /metrics. Unset disables both endpoints. It is separate from project API tokens. |
DISABLE_VERSION_CHECK |
false |
Set to true to disable the update check. Otherwise the server contacts https://www.tindra.sh/version-update-check every six hours and displays available updates in Settings > Overview. |
BILLING_URL |
Unset | Billing portal link shown in settings and event-limit banners when configured. |
SKIP_AUTO_MIGRATE |
false |
Advanced: skip startup migrations. You must apply the target binary's migrations before serving traffic. See Upgrades. |
/metrics exposes Prometheus ingestion counters and queue gauges using Authorization: Bearer <STATS_API_KEY>. It returns 404 when disabled and 401 for incorrect credentials. Its authentication does not need a database connection, so a database outage does not itself hide queue metrics. Counters reset when the process restarts.
The JSON status endpoint /api/instance/ingestion instead requires a user session with manage_projects; neither an operator metrics key nor a project API token grants that session permission.
Set EMAIL_PROVIDER to enable outbound email for alerts and invitations. Leave it unset to disable email entirely.
Common
| Variable | Description |
|---|---|
EMAIL_PROVIDER |
One of: smtp, postmark, brevo, ahasend, lettermint, cloudflare |
EMAIL_FROM |
Required when EMAIL_PROVIDER is set; sender address, e.g. alerts@example.com. |
EMAIL_FROM_NAME |
Optional Brevo sender display name; defaults to EMAIL_FROM. |
SMTP
| Variable | Description |
|---|---|
SMTP_HOST |
SMTP server hostname |
SMTP_PORT |
Defaults to 587 (STARTTLS); use 465 for implicit TLS. |
SMTP_USERNAME |
SMTP username |
SMTP_PASSWORD |
SMTP password |
Postmark
| Variable | Description |
|---|---|
POSTMARK_API_KEY |
Server API token from the Postmark dashboard |
Brevo
| Variable | Description |
|---|---|
BREVO_API_KEY |
API key from the Brevo dashboard |
AhaSend
| Variable | Description |
|---|---|
AHASEND_API_KEY |
API key from your AhaSend account |
Lettermint
| Variable | Description |
|---|---|
LETTERMINT_API_KEY |
API key from the Lettermint dashboard |
Cloudflare Email Routing
| Variable | Description |
|---|---|
CLOUDFLARE_EMAIL_API_TOKEN |
Cloudflare API token with Email Routing write permissions |
CLOUDFLARE_ACCOUNT_ID |
Your Cloudflare account ID |
OAuth / SSO
All values below are unset by default. Configure the callback base and the complete settings for at least one provider before restarting. See Authentication for admission, verified-email linking, Microsoft setup, MFA, and recovery.
| Variable | Description |
|---|---|
OAUTH_REDIRECT_BASE |
Public URL used to construct OAuth callbacks. |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET |
GitHub OAuth app credentials. |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
Google OAuth client credentials. |
MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET |
Microsoft application credentials. |
MICROSOFT_TENANT |
Required concrete Directory tenant UUID for Microsoft; shared tenant aliases are unsupported. |
AUTH0_DOMAIN, AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET |
Auth0 domain and application credentials. |
ZITADEL_ISSUER_URL, ZITADEL_CLIENT_ID, ZITADEL_CLIENT_SECRET |
Zitadel issuer and application credentials. |
OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET |
Generic OIDC issuer and client credentials. |
OIDC_PROVIDER_NAME |
Defaults to oidc; determines provider identity, callback path, and login label. |
A nonempty callback base, client ID/secret, issuer URL, or Auth0 domain selects SSO-only authentication even if discovery fails. Local password login, password invitation acceptance, and reset redemption stay disabled until SSO configuration is intentionally removed and the server restarted. A provider name or Microsoft tenant alone is not a complete SSO configuration.
Docker Compose example
This is a service fragment; see Self-Hosting for the full stack. Supply a URL-safe database password through Compose's .env file and keep it consistent with the Postgres service.
services:
tindra:
stop_grace_period: 75s
environment:
DATABASE_URL: "postgres://tindra:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}@postgres:5432/tindra?sslmode=disable"
PUBLIC_URL: https://tindra.example.com
BIND_ADDR: ":8080"
DATA_DIR: /data
COOKIE_SECURE: "true"
RETENTION_DAYS: "90"
PROFILE_RETENTION_DAYS: "7"
PROFILE_STORAGE_LIMIT_MB: "2048"
EMAIL_PROVIDER: smtp
EMAIL_FROM: alerts@example.com
SMTP_HOST: mail.example.com
SMTP_PORT: "587"
SMTP_USERNAME: alerts@example.com
SMTP_PASSWORD: "${SMTP_PASSWORD:?Set SMTP_PASSWORD}"
POSTGRES_PASSWORD and PORT are inputs to the production Compose example, not Tindra binary settings. The password is required and has no built-in fallback; PORT defaults to 8080. Production Postgres is not published to the host, and the old POSTGRES_PORT mapping is no longer used. stop_grace_period is a Compose setting, not an environment variable.
Monitor ingestion health
Use Ingestion Monitoring for authenticated Prometheus scrapes, queue and write metrics, and a recovery checklist when accepted data is not appearing. Pending in-memory data is not part of a database backup.