Skip to main content
doc0blade47/next.jsdocumentation home
Sign in

Telemetry

Last updated Oct 12, 2026100% grounded
Relevant source files

The following files were used as context for generating this wiki page:

Overview

Next.js telemetry provides an anonymous data collection system designed to capture usage patterns, feature adoption, compilation results, and performance diagnostics, helping shape product roadmaps and prioritize features. Operating via persistent local configuration stores and secure identity hashing, the telemetry subsystem records events locally, buffers payloads in memory, and dispatches them securely over HTTPS to upstream collection endpoints while preserving end-user privacy. Sources: packages/next/src/telemetry/storage.ts:14-30, packages/next/src/telemetry/storage.ts:346-360, packages/next/src/telemetry/post-telemetry-payload.ts:25-29

The infrastructure integrates deeply across the build pipeline, compiler plugins, CLI execution lifecycle, and OpenTelemetry tracing bridges to gather granular insights. It incorporates robust opt-out mechanisms, background flushing workers for process lifecycles, and environment variable controls to ensure telemetry collection remains transparent and fully controllable. Sources: packages/next/src/build/index.ts:4902-4911, packages/next/src/telemetry/detached-flush.ts:51-53, packages/next/src/cli/next-telemetry.ts:14-22

Telemetry storage architecture and identity hashing

Core Telemetry Class and Configuration Storage

The Telemetry class coordinates local event recording, persistent configuration management, and payload dispatch. During instantiation, it reads environment variables (NEXT_TELEMETRY_DISABLED and NEXT_TELEMETRY_DEBUG) to determine operating modes and initializes a storage directory using getStorageDirectory. Ephemeral environments such as continuous integration servers or Docker containers resolve storage to path.join(distDir, 'cache'), while standard developer workstations omit a dedicated local directory override. The class wraps Next.js's compiled Conf library under the project name nextjs, catching permission initialization errors to prevent process crashes. A cryptographically secure session identifier (sessionId) is generated using 32 random bytes converted to a hex string, while an in-memory queue (Set<Promise<RecordObject>>) tracks active submission promises. Sources: packages/next/src/telemetry/storage.ts:40-89

Constant NameValue / KeyPurpose
TELEMETRY_KEY_ENABLEDtelemetry.enabledStores whether telemetry collection is enabled or disabled.
TELEMETRY_KEY_NOTIFY_DATEtelemetry.notifiedAtSpecifies when the user was first informed about anonymous telemetry.
TELEMETRY_KEY_IDtelemetry.anonymousIdQuasi-persistent identifier used to deduplicate recurring events anonymously.
TELEMETRY_KEY_SALTtelemetry.saltCryptographic salt included within hashed values to prevent dictionary lookups.

Sources: packages/next/src/telemetry/storage.ts:14-30

Note

The Conf persistence layer handles initialization permission errors via a try-catch block, falling back to a null configuration reference to prevent unexpected application crashes during startup. Sources: packages/next/src/telemetry/storage.ts:75-82

Anonymous Identity and Hashing Mechanics

Persistent user identification relies on the anonymousId getter, which checks the local configuration store for telemetry.anonymousId. If absent, it generates a new 32-byte random hex string and caches it in the configuration store. Project identification utilizes getRawProjectId, pairing local configuration contexts with raw project identifiers to scope events. To maintain user privacy, all sensitive payloads pass through irreversible hashing mechanisms incorporating a per-installation cryptographic salt (telemetry.salt). This salt value is never transmitted upstream, neutralizing pre-computed dictionary attacks and ensuring that submitted telemetry data remains strictly non-identifiable. Sources: packages/next/src/telemetry/storage.ts:11-29, packages/next/src/telemetry/storage.ts:120-131

Anonymous system metadata collection

Host Platform and Machine Indicators

The getAnonymousMeta function gathers execution context and environment metadata by inspecting host operating system interfaces and environment variables. Host metrics include the platform type, release version, system architecture, CPU core count, primary CPU model, and CPU speed via the Node.js os module. Memory capacity is calculated by converting total system memory from bytes to megabytes and rounding to the nearest integer. Sources: packages/next/src/telemetry/anonymous-meta.ts:3-43

Trait PropertySource / ExpressionDescription
systemPlatformos.platform()Operating system platform identifier.
systemReleaseos.release()Operating system release version.
systemArchitectureos.arch()System CPU architecture.
cpuCountcpus.lengthTotal number of available CPU cores.
cpuModelcpus.length ? cpus[0].model : nullModel designation of the primary CPU.
cpuSpeedcpus.length ? cpus[0].speed : nullClock speed of the primary CPU in megahertz.
memoryInMbMath.trunc(os.totalmem() / Math.pow(1024, 2))Total system memory truncated to megabytes.

Sources: packages/next/src/telemetry/anonymous-meta.ts:35-43

Note

Metadata payloads are cached in the module-scoped traits variable upon initial resolution, ensuring that subsequent telemetry submissions reuse identical payload metadata without re-querying system interfaces. Sources: packages/next/src/telemetry/anonymous-meta.ts:25-30

Environment and Container Detection

Containerization, continuous integration services, and developer tooling indicators are collected alongside core machine statistics. Container presence is evaluated using is-docker, Windows Subsystem for Linux detection relies on is-wsl, and local development environments verify whether NOW_REGION is set to 'dev1'. Continuous integration status and vendor identity are resolved via ../server/ci-info, while package manager or execution agent names are populated asynchronously using getAgentName(). The active framework release is captured directly from process.env.__NEXT_VERSION. Sources: packages/next/src/telemetry/anonymous-meta.ts:1-22, packages/next/src/telemetry/anonymous-meta.ts:44-52

Event queuing and synchronous flush

In-Memory Buffering and Queue State

The Telemetry class maintains an in-memory event queue represented as a Set of promises tracking asynchronous record operations (this.queue = new Set()). When telemetry is recorded, single events or arrays of events are normalized into a TelemetryEvent array. If debugging is enabled via NEXT_TELEMETRY_DEBUG, events bypass network dispatch and instead log the serialized payload alongside resolved anonymous metadata to standard error after a short timeout. Sources: packages/next/src/telemetry/storage.ts:31-38, packages/next/src/telemetry/storage.ts:59-60, packages/next/src/telemetry/storage.ts:306-338

Warning

Telemetry recording returns an immediate resolved promise if the feature is globally disabled via this.isDisabled or if an empty event array is provided, preventing unnecessary allocations or execution overhead. Sources: packages/next/src/telemetry/storage.ts:316-318, packages/next/src/telemetry/storage.ts:341-343

HTTPS Payload Dispatch and Retry Policies

Payload submission is handled by postNextTelemetryPayload, which dispatches HTTPS requests to the telemetry collection endpoint via the Fetch API using next/dist/compiled/async-retry. If an incoming AbortSignal is absent and AbortSignal.timeout is supported, a default 5000-millisecond timeout signal is instantiated. The request configuration options and retry policies are structured explicitly to constrain network latency during dispatch operations. Sources: packages/next/src/telemetry/post-telemetry-payload.ts:1-38, packages/next/src/telemetry/storage.ts:345-365

ParameterValueDescription
Endpoint URLhttps://telemetry.nextjs.org/api/v1/recordDestination HTTPS endpoint for telemetry payloads.
HTTP MethodPOSTHTTP method used for dispatching event data.
Content-Typeapplication/jsonRequest header specifying JSON payload formatting.
minTimeout500Initial retry delay in milliseconds.
retries1Maximum number of retry attempts upon failure.
factor1Exponential backoff factor applied between retries.

Sources: packages/next/src/telemetry/post-telemetry-payload.ts:23-38

Note

Network errors and non-OK HTTP responses are caught and swallowed silently to guarantee that telemetry failures never disrupt or crash the main application process. Sources: packages/next/src/telemetry/post-telemetry-payload.ts:30-47

Global telemetry flushing is coordinated across modules through flushTelemetry, which retrieves the active telemetry instance from traceGlobals and awaits its flush routine. Sources: packages/next/src/telemetry/flush-telemetry.ts:1-9

Detached background telemetry flushing

Argument Parsing and Execution Validation

The detached background telemetry flushing mechanism is implemented as an immediately invoked asynchronous function in packages/next/src/telemetry/detached-flush.ts. It expects process arguments to follow a precise ordering: mode, project directory, an optional events filename, and an optional resolved distribution directory. Sources: packages/next/src/telemetry/detached-flush.ts:9-15

if (!dir || mode !== 'dev') {
  throw new Error(
    `Invalid flags should be run as node detached-flush dev ./path-to/project [eventsFile] [distDir]`
  )
}

Sources: packages/next/src/telemetry/detached-flush.ts:18-22

dir = getProjectDir(dir)

Sources: packages/next/src/telemetry/detached-flush.ts:23-23

Warning

If inputDir is omitted or mode is not 'dev', the script throws an error enforcing the exact node invocation syntax. Sources: packages/next/src/telemetry/detached-flush.ts:18-22

Distribution Directory and Event Batch Resolution

To avoid reloading phase-specific configuration when build nudges pass their resolved output directories, the script conditionally resolves distDir. Named batches live in the .next/cache directory to prevent build cleanup from removing them prior to submission, while callers without an events filename fall back to the legacy root _events.json location. Sources: packages/next/src/telemetry/detached-flush.ts:25-38

// Build nudges pass their resolved output directory to avoid loading dev config again.
const distDir = suppliedDistDir
  ? path.resolve(suppliedDistDir)
  : path.join(
      dir,
      (await loadConfig(PHASE_DEVELOPMENT_SERVER, dir)).distDir || '.next'
    )
 
// Named batches live in cache so build cleanup cannot remove them before submission.
// Retain the legacy root path for callers without an events filename.
const eventsPath =
  eventsFile && !eventsFile.includes('/')
    ? path.join(distDir, 'cache', eventsFile)
    : path.join(distDir, '_events.json')

Sources: packages/next/src/telemetry/detached-flush.ts:25-38

Note

If the events file does not exist (ENOENT), the worker catches the error code and exits immediately with code 0 since there are no events to process. Sources: packages/next/src/telemetry/detached-flush.ts:41-49

Event Recording, Flushing, and Cleanup

Once the event payload is read and parsed as JSON, a new Telemetry instance is initialized using the resolved distDir. The worker records the events, initiates a flush, unlinks the events file upon completion, and allows Node.js to exit naturally. Sources: packages/next/src/telemetry/detached-flush.ts:51-59

  const telemetry = new Telemetry({ distDir })
  await telemetry.record(events)
  await telemetry.flush()
 
  // finished flushing events clean-up
  fs.unlinkSync(eventsPath)
  // Don't call process.exit() here - let Node.js exit naturally after
  // all pending work completes (e.g., setTimeout in debug telemetry)
})()

Sources: packages/next/src/telemetry/detached-flush.ts:51-59

Compilation and feature adoption events

Build Outcomes and Optimization Telemetry

Next.js records comprehensive telemetry during compilation phases via functions defined in packages/next/src/telemetry/events/build.ts. The eventBuildCompleted and eventBuildOptimize factories evaluate page paths against regex rules to categorize project layout traits, specifically identifying test files or directories via /[\\/]__(tests|mocks)__[\\/]/i or /\.(?:spec|test)\.[^.]+$/i, and dunder directories via /[\\/]__[^\\/]+(?<![\\/]__(?:tests|mocks))__[\\/]/i. Sources: packages/next/src/telemetry/events/build.ts:5-108, packages/next/src/telemetry/events/build.ts:151-178

const EVENT_BUILD_COMPLETED = 'NEXT_BUILD_COMPLETED'
type EventBuildCompleted = {
  bundler: 'webpack' | 'rspack' | 'turbopack'
  durationInSeconds: number
  totalPageCount: number
  hasDunderPages: boolean
  hasTestPages: boolean
  totalAppPagesCount?: number
}

Sources: packages/next/src/telemetry/events/build.ts:76-84

Note

NEXT_BUILD_OPTIMIZED aggregates detailed route metrics including static, server, SSR, and edge runtime counts for both pages and App Router directories, alongside route matcher counts like headersCount, rewritesCount, and redirectsCount. Sources: packages/next/src/telemetry/events/build.ts:124-149

SWC Feature Detection and Compiler Failures

The TelemetryPlugin inspects the Webpack ModuleGraph during compilation by tapping into module finish hooks to resolve incoming connections and count unique origin imports for specific features. When next-swc native binding loads fail, eventSwcLoadFailure gathers host runtime diagnostics including glibcVersion from process.report?.getReport().header.glibcVersionRuntime and installed optional SWC package versions. Sources: packages/next/src/build/webpack/plugins/telemetry-plugin/telemetry-plugin.ts:168-235, packages/next/src/telemetry/events/swc-load-failure.ts:21-55

telemetry.record({
  eventName: EVENT_PLUGIN_PRESENT,
  payload: {
    nextVersion: process.env.__NEXT_VERSION as string,
    glibcVersion,
    installedSwcPackages,
    arch: process.arch,
    platform: process.platform,
    nodeVersion: process.versions.node,
    wasm: event?.wasm,
    nativeBindingsErrorCode: event?.nativeBindingsErrorCode,
  },
})
// ensure this event is flushed before process exits
await telemetry.flush()

Sources: packages/next/src/telemetry/events/swc-load-failure.ts:57-71

Feature NameModule Path or Regexp IdentifierCategory
next/image/next/image.jsBuilt-in Component
next/future/image/next/future/image.jsBuilt-in Component
next/legacy/image/next/legacy/image.jsBuilt-in Component
next/script/next/script.jsBuilt-in Component
next/dynamic/next/dynamic.jsBuilt-in Component
@next/font/google/\/@next\/font\/google\/target\.css\?.+$/Font Optimization
@next/font/local/\/@next\/font\/local\/target\.css\?.+$/Font Optimization
next/font/google/\/next\/font\/google\/target\.css\?.+$/Font Optimization
next/font/local/\/next\/font\/local\/target\.css\?.+$/Font Optimization

Sources: packages/next/src/build/webpack/plugins/telemetry-plugin/telemetry-plugin.ts:65-77

Warning

eventErrorThrown intentionally strips out error message contents to protect user privacy, recording solely the error class name (errorCode) and any anonymized internal source tree location path. Sources: packages/next/src/telemetry/events/build.ts:311-326

CLI lifecycle and upgrade telemetry

Session Stop Events and MCP Tool Tracking

Next.js monitors CLI usage by capturing session termination via eventCliSessionStopped, which records environment identifiers and directory structures under NEXT_CLI_SESSION_STOPPED. Version checking relies strictly on process.env.__NEXT_VERSION; if unpopulated, payload emission is aborted. Alongside core CLI metrics, McpTelemetryTracker records tool invocation counts for Model Context Protocol features. Sources: packages/next/src/server/mcp/mcp-telemetry-tracker.ts:1-47, packages/next/src/telemetry/events/session-stopped.ts:1-39

const payload: EventCliSessionStopped = {
  nextVersion: process.env.__NEXT_VERSION,
  nodeVersion: process.version,
  cliCommand: event.cliCommand,
  durationMilliseconds: event.durationMilliseconds,
  ...(typeof event.turboFlag !== 'undefined'
    ? {
        turboFlag: !!event.turboFlag,
      }
    : {}),
  pagesDir: event.pagesDir,
  appDir: event.appDir,
  isRspack: process.env.NEXT_RSPACK !== undefined,
}

Sources: packages/next/src/telemetry/events/session-stopped.ts:25-38

Payload FieldTypeDescription
cliCommandstringThe invoked CLI command name
nextVersionstringResolved from process.env.__NEXT_VERSION
nodeVersionstringResolved from process.version
turboFlagboolean | nullIndicates if Turbopack was requested
durationMillisecondsnumber | nullTotal session execution duration
pagesDirbooleanPresence of a pages directory
appDirbooleanPresence of an app directory
isRspackbooleanResolved from process.env.NEXT_RSPACK !== undefined

Sources: packages/next/src/telemetry/events/session-stopped.ts:3-12, packages/next/src/telemetry/events/session-stopped.ts:25-38

Warning

eventCliSessionStopped acts as an invariant check for build tooling: if process.env.__NEXT_VERSION is not a string, the event array is dropped entirely rather than emitting incomplete version metadata. Sources: packages/next/src/telemetry/events/session-stopped.ts:20-23

Agent Upgrade and Nudge Telemetry

The telemetry infrastructure tracks automated upgrade policies, nudge prompts, and agent execution results using structured event factories. Every generated event wraps its payload with schemaVersion: 1. Policy detection, user prompt interactions, run initiations, and CLI results are emitted via distinct event channels to separate configuration reach from operational outcomes. Sources: packages/next/src/telemetry/events/agent-upgrade.ts:28-97

Type IdentifierAllowed Values / StructurePurpose
AgentUpgradePolicy'security', 'latest', 'experimental-future'Defines upgrade strictness
AgentUpgradeOrigin'human_manual', 'human_nudge', 'agent_manual', 'agent_nudge'Tracks how an upgrade run started
AgentUpgradeHandoffMethod'existing_agent', 'launched_agent', 'copied_prompt', 'printed_prompt'Describes prompt delivery mechanism
AgentUpgradeCLIResult'no_update_needed', 'no_safe_target', 'metadata_failure', 'guide_failure', 'cancelled', 'handoff_issued', 'handoff_failed', 'cli_failure'Reports CLI preparation and delivery result

Sources: packages/next/src/telemetry/events/agent-upgrade.ts:4-27

Tip

eventAgentUpgradeCLIResult captures the CLI's preparation and prompt delivery stages, whereas eventAgentUpgradeAgentResult separately records the agent's verified execution outcome (success or failure) and its resulting version. Sources: packages/next/src/telemetry/events/agent-upgrade.ts:78-97

Trace span telemetry reporting

Next.js bridges internal Trace Profiling spans into the telemetry dispatcher via reportToTelemetry within Trace Profiling. The bridge maps incoming trace event names against a fixed access list, filtering out unlisted metrics before querying global telemetry state. When an active telemetry instance is retrieved from traceGlobals, matching trace events are recorded with their execution duration converted into microseconds. Sources: packages/next/src/trace/report/to-telemetry.ts:5-27

Trace Event NameMapped Telemetry Event NamePayload Structure
webpack-invalidatedWEBPACK_INVALIDATED{ durationInMicroseconds: number }

Sources: packages/next/src/trace/report/to-telemetry.ts:5-9, packages/next/src/trace/report/to-telemetry.ts:21-26

Note

TRACE_EVENT_ACCESSLIST acts as an explicit allowlist: any trace event whose name is absent from the lookup map is silently ignored, preventing arbitrary internal build spans from leaking into telemetry payloads. Sources: packages/next/src/trace/report/to-telemetry.ts:5-15

To coordinate multiple diagnostic sinks alongside telemetry routing, MultiReporter implements the Reporter interface. It broadcasts trace events, flush operations, and close requests concurrently to an underlying array of reporters, which includes JSON diagnostics, build JSON reports, and telemetry dispatchers. Sources: packages/next/src/trace/report/index.ts:7-32

Next.js provides global consent controls through configuration storage and environment overrides. The Telemetry class constructor inspects process.env.NEXT_TELEMETRY_DISABLED to determine whether collection is blocked. Sources: packages/next/src/telemetry/storage.ts:56-70

Persistent user preferences are maintained using a configuration store initialized under the project distribution directory or cache storage. Sources: packages/next/src/telemetry/storage.ts:40-82

The next telemetry command interface handles user prompt interactions and preference modifications directly from the command line. Sources: packages/next/src/cli/next-telemetry.ts:14-51

Command Option / FlagAction PerformedResult Output Message
enableSets internal telemetry preference to enabled via telemetry.setEnabled(true)Success! followed by status confirmation
disableSets internal telemetry preference to disabled via telemetry.setEnabled(false)Your preference has been saved to [path]. or notice that collection is already disabled
noneDisplays current telemetry configuration status and explanationNext.js Telemetry title followed by current status block

Sources: packages/next/src/cli/next-telemetry.ts:14-51

Note

When telemetry is newly initialized without a stored notification date, the Telemetry constructor triggers this.notify() to print an informational attention message to standard output unless skipNotify is set to true. Sources: packages/next/src/telemetry/storage.ts:61-118

Was this page helpful?