How Self-Host Telemetry Works in Open SEO and How to Opt Out

Self-host telemetry in Open SEO sends anonymous, aggregated usage data via a periodic heartbeat that can be completely disabled by setting the SELF_HOST_TELEMETRY=off environment variable.

The every-app/open-seo repository includes a lightweight telemetry system for self-hosted deployments that helps maintainers understand usage patterns without compromising privacy. This system aggregates counts locally in the database and transmits only high-level metrics, with a straightforward opt-out mechanism controlled via environment configuration.

How the Telemetry System Works

Open SEO's telemetry pipeline operates through three core components: a periodic heartbeat generator, a local state store, and an optional MCP tool call counter.

The Heartbeat Mechanism

At server startup, the application invokes maybeSendSelfHostHeartbeat from src/server/lib/self-host-telemetry.ts. This function reads the current telemetry state from the telemetry_state database table and transmits an anonymous payload to the telemetry endpoint. The heartbeat runs immediately on boot and continues on a scheduled interval via setInterval in src/server.ts.

The payload includes:

  • A randomly generated install ID created on first run
  • Timestamps (installedAt, lastHeartbeatAt)
  • Aggregated counts of installs, users, projects, and feature usage
  • Failed setup-check names and their current statuses

If the remote endpoint is unreachable, the function catches the network error, logs a debug message, and retries on the next scheduled run without dropping data.

Data Privacy and Aggregation

All telemetry data is aggregated numerically before transmission. The system explicitly excludes sensitive information such as URLs, search keywords, AI prompts, email addresses, or IP-derived location data. Data is stored locally in the telemetry_state table (defined in src/db/telemetry.schema.ts) until successfully transmitted, ensuring the instance maintains an accurate record of its own usage patterns even during network outages.

MCP Tool Call Tracking

For instances utilizing MCP features, the system tracks tool usage through incrementSelfHostMcpToolCallCount. This function, imported in instrumentation files like src/server/mcp/instrumentation.ts, increments the mcpToolCallCount column in the local telemetry_state row each time an MCP tool executes. This cumulative count is included in the next successful heartbeat, providing maintainers with insight into feature adoption without exposing specific tool inputs or outputs.

Disabling Telemetry (Opt-Out)

Telemetry is enabled by default to support project health monitoring, but operators can disable it completely through environment configuration.

Environment Variable Configuration

The primary opt-out mechanism uses the SELF_HOST_TELEMETRY environment variable. When set to any of the following values, telemetry is disabled: off, 0, false, or no.

The check occurs in src/shared/selfhost-checks.ts via the telemetryDisabled() function. When this function returns true, maybeSendSelfHostHeartbeat returns immediately without network requests, and incrementSelfHostMcpToolCallCount becomes a no-op.


# Disable telemetry

export SELF_HOST_TELEMETRY=off

# Enable telemetry (default behavior)

export SELF_HOST_TELEMETRY=on

Docker and Cloudflare Deployments

For Docker-based deployments, add the environment variable to your .env file or compose.yaml:


# compose.yaml

services:
  open-seo:
    env_file: .env
    environment:
      - SELF_HOST_TELEMETRY=off

For Cloudflare Worker deployments, set the variable in the vars section of your configuration. The Docker self-hosting documentation at docs/SELF_HOSTING_DOCKER.md provides additional context for containerized environments.

Verification

To verify telemetry is disabled, check the server logs on startup. When SELF_HOST_TELEMETRY=off is set, the heartbeat initialization code in src/server.ts will skip the initial maybeSendSelfHostHeartbeat call and the interval timer, leaving no outbound telemetry traffic.

Key Source Files and Implementation Details

Understanding the telemetry implementation requires familiarity with these specific files in the every-app/open-seo repository:

  • src/server/lib/self-host-telemetry.ts – Contains maybeSendSelfHostHeartbeat, incrementSelfHostMcpToolCallCount, and the logic for reading/writing the telemetry_state table.
  • src/shared/selfhost-checks.ts – Houses the telemetryDisabled() function that evaluates the SELF_HOST_TELEMETRY environment variable.
  • src/db/telemetry.schema.ts – Defines the database schema for the telemetry_state table, including columns for installId, mcpToolCallCount, and heartbeat timestamps.
  • src/server.ts – Orchestrates the initial heartbeat call and schedules subsequent transmissions via setInterval.
  • docs/SELF_HOSTING_DOCKER.md – Documents the telemetry policy and environment variable configuration for Docker users.

Summary

  • Open SEO's self-host telemetry transmits only anonymous, aggregated counts via a periodic heartbeat mechanism controlled by maybeSendSelfHostHeartbeat.
  • Data collected includes install IDs, timestamps, user/project counts, and MCP tool call tallies—never URLs, prompts, or identifying information.
  • The system stores state locally in the telemetry_state database table and gracefully handles network failures with retry logic.
  • Opt-out requires setting the SELF_HOST_TELEMETRY environment variable to off, 0, false, or no, which causes telemetryDisabled() to return true and short-circuit all telemetry functions.
  • Disabling telemetry has no impact on core functionality; it simply prevents the transmission of usage statistics.

Frequently Asked Questions

What data does Open SEO telemetry collect?

Open SEO collects only aggregated numerical data: a random install ID, installation and heartbeat timestamps, counts of users, projects, and feature usage, and names of failed setup checks. It does not collect URLs, search queries, AI prompts, email addresses, or IP addresses. All data is aggregated locally in the telemetry_state table before transmission.

How do I completely disable telemetry in a Docker deployment?

Set the environment variable SELF_HOST_TELEMETRY=off in your .env file or directly in your compose.yaml environment section. Restart the container to apply the change. The server will detect this via src/shared/selfhost-checks.ts and disable all heartbeat transmissions and MCP call counting.

Will disabling telemetry affect my instance's functionality?

No. Disabling telemetry only prevents the maybeSendSelfHostHeartbeat function from executing network requests and stops the incrementSelfHostMcpToolCallCount counter. All core Open SEO features, including MCP tools, search functionality, and project management, continue to operate normally without the telemetry component.

Where is the telemetry state stored before transmission?

The telemetry state persists in your local database in the telemetry_state table, defined in src/db/telemetry.schema.ts. This table stores the install ID, cumulative MCP tool call counts, and timestamps. Data remains local until successfully transmitted; if the remote endpoint is unreachable, the state persists for the next heartbeat attempt.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →