# Monitor a Next.js app with OpenTelemetry

> Add instrumentation.ts with @vercel/otel and set the OTLP environment variables. Next.js App Router requests and server errors show up in Lampwick.

Next.js has OpenTelemetry built in: with an `instrumentation.ts` file that calls `@vercel/otel`, every request and every server-side error is exported. Point it at Lampwick with environment variables. The general reference is [OpenTelemetry](/docs/opentelemetry/).

Tested with Next.js 16.4 (App Router, `next build` and `next start`), `@vercel/otel` 2.3.0, `@opentelemetry/instrumentation-pg` 0.76.0 and Node.js 26.9, on 10 November 2026.

## Requirements

- Next.js 15 or later (`instrumentation.ts` needs no config flag from 15 on). We tested 16.4.
- The project's ingest token, from the **Connect your app** card on the project's **Settings** page. Only team owners see it.

## Let your agent do it

Paste this into your coding agent, in the app's repository:

```text
Add OpenTelemetry to this Next.js app so it reports to Lampwick: follow https://lampwick.dev/docs/nextjs.md exactly (instrumentation.ts with @vercel/otel, the environment variables, PgInstrumentation only if the app uses pg). Read the ingest token from the environment, never commit it.
```

## Install the packages

```bash
npm install @vercel/otel @opentelemetry/api @opentelemetry/api-logs @opentelemetry/instrumentation @opentelemetry/sdk-logs
```

## Add instrumentation.ts

Create `instrumentation.ts` in the project root (next to `app/`, or in `src/` if your app lives there):

```ts
import { registerOTel } from '@vercel/otel';

export function register() {
  registerOTel({ serviceName: 'my-app' });
}
```

## Set the environment variables

```dotenv
OTEL_EXPORTER_OTLP_ENDPOINT=https://app.lampwick.dev
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS=x-lampwick-token=YOUR_PROJECT_TOKEN
OTEL_METRICS_EXPORTER=none
```

`YOUR_PROJECT_TOKEN` is the project's ingest token. On Vercel, add them under **Settings → Environment Variables**; locally, in `.env.local`.

## Database queries

`@vercel/otel` instruments `fetch` only. For `pg`, add its instrumentation. The current `@opentelemetry/instrumentation-pg` needs a newer `@opentelemetry/api` than `@vercel/otel` 2.3 allows, so install 0.76.0:

```bash
npm install @opentelemetry/instrumentation-pg@0.76.0
```

```ts
import { registerOTel } from '@vercel/otel';
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';

export function register() {
  registerOTel({
    serviceName: 'my-app',
    instrumentations: ['auto', new PgInstrumentation()],
  });
}
```

## The app we tested

A route handler with a query, and a page that throws:

```ts
// app/orders/[id]/route.ts
import { Pool } from 'pg';

const db = new Pool({ connectionString: process.env.DATABASE_URL });

export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const { rows } = await db.query('SELECT id, total FROM orders WHERE id = $1', [id]);
  return Response.json(rows[0] ?? null);
}
```

```tsx
// app/boom/page.tsx
export const dynamic = 'force-dynamic';

export default async function Boom() {
  throw new Error('Checkout failed for order 42');
}
```

## Check that it works

Run `next build` and `next start`, then open `/orders/1`, `/orders/2` and `/boom`. Within about 5 seconds the project shows:

- **Requests**: `GET /orders/[id]` (2) and `GET /boom` (1, status 500).
- **Queries**: `SELECT id, total FROM orders WHERE id = $1`, linked to its request (with `PgInstrumentation`).
- **Issues**: `Error: Checkout failed for order 42`, unhandled, in the `/boom` request.

Next.js also traces its own startup and module loading. Lampwick leaves those spans out.

## What's missing

- **Stack traces point into `.next/server/chunks/`.** Next.js bundles server code and Lampwick doesn't apply source maps, so the issue names the chunk file and function names may be minified. The message and the request it happened in are exact.
- **Logs aren't sent.** `console.log` isn't captured, and `@vercel/otel` exports no logs by default. We haven't tested a logging setup with Next.js yet.
- **Client-side errors** happen in the browser, not on the server. Add the [browser errors snippet](/docs/browser/) for those.
- **Edge runtime**: we tested the Node.js runtime only.

## Troubleshooting

| What you see | Cause and fix |
|---|---|
| Nothing arrives, and nothing is logged | Next.js only calls `register()` from `instrumentation.ts` in the project root, or in `src/` when the app lives there. Move the file. A `401` or another export error: see the [OpenTelemetry troubleshooting](/docs/opentelemetry/#troubleshooting). |
| `npm install` fails with `ERESOLVE` on `@opentelemetry/api` | A current OpenTelemetry instrumentation package wants a newer API than `@vercel/otel` allows. Pin a version built on `@opentelemetry/instrumentation` 0.2xx, such as `@opentelemetry/instrumentation-pg@0.76.0`. |
| `Specified module format (CommonJs) is not matching` during `next build` | `package.json` has `"type": "commonjs"` (what `npm init -y` writes). Remove the line. |
| Requests arrive, queries don't | Add `PgInstrumentation` (or the instrumentation for your database client) as above. |
