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

> Learn how self-host telemetry works in Open SEO and easily opt out by setting the SELF_HOST_TELEMETRY environment variable to off. Control your data privacy.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-09-12

---

**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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.

```bash

# 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`](https://github.com/every-app/open-seo/blob/main/compose.yaml):

```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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts)** – Houses the `telemetryDisabled()` function that evaluates the `SELF_HOST_TELEMETRY` environment variable.
- **[`src/db/telemetry.schema.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/src/server.ts)** – Orchestrates the initial heartbeat call and schedules subsequent transmissions via `setInterval`.
- **[`docs/SELF_HOSTING_DOCKER.md`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/compose.yaml) environment section. Restart the container to apply the change. The server will detect this via [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.