From a75c1fc833e42180cbc4b4643d2347c2f7372db8 Mon Sep 17 00:00:00 2001 From: Santiago Mola Date: Wed, 29 Apr 2026 11:32:36 +0200 Subject: [PATCH 1/2] [nodejs] use exec to run nextjs --- utils/build/docker/nodejs/nextjs.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/build/docker/nodejs/nextjs.Dockerfile b/utils/build/docker/nodejs/nextjs.Dockerfile index 824d58639b8..64eb26877fc 100644 --- a/utils/build/docker/nodejs/nextjs.Dockerfile +++ b/utils/build/docker/nodejs/nextjs.Dockerfile @@ -24,6 +24,6 @@ ENV DD_DATA_STREAMS_ENABLED=true ENV PORT=7777 ENV HOSTNAME=0.0.0.0 COPY utils/build/docker/nodejs/app.sh app.sh -RUN printf './node_modules/.bin/next start' >> app.sh ENV NODE_OPTIONS="--import dd-trace/initialize.mjs" -CMD ./app.sh +RUN printf 'exec ./node_modules/.bin/next start' >> app.sh +CMD ["./app.sh"] From 6bd6d14e13efaaed90832d215fa48c043dfb1a7e Mon Sep 17 00:00:00 2001 From: Santiago Mola Date: Mon, 27 Apr 2026 11:16:50 +0200 Subject: [PATCH 2/2] [nodejs] Handle Next.js weblog shutdown telemetry --- utils/build/docker/nodejs/nextjs.Dockerfile | 5 +++ .../build/docker/nodejs/nextjs/next.config.js | 6 +++- .../nodejs/nextjs/src/instrumentation.js | 33 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 utils/build/docker/nodejs/nextjs/src/instrumentation.js diff --git a/utils/build/docker/nodejs/nextjs.Dockerfile b/utils/build/docker/nodejs/nextjs.Dockerfile index 64eb26877fc..b242eca4e3a 100644 --- a/utils/build/docker/nodejs/nextjs.Dockerfile +++ b/utils/build/docker/nodejs/nextjs.Dockerfile @@ -21,6 +21,11 @@ ENV DD_TRACE_HEADER_TAGS=user-agent # docker startup ENV DD_DATA_STREAMS_ENABLED=true +# Let src/instrumentation.js handle Next.js shutdown signals manually, as documented in: +# https://nextjs.org/docs/pages/guides/self-hosting#manual-graceful-shutdowns +# Support was added in https://github.com/vercel/next.js/pull/59117; this +# weblog is pinned to Next.js >=14.0.4 to support NEXT_MANUAL_SIG_HANDLE. +ENV NEXT_MANUAL_SIG_HANDLE=true ENV PORT=7777 ENV HOSTNAME=0.0.0.0 COPY utils/build/docker/nodejs/app.sh app.sh diff --git a/utils/build/docker/nodejs/nextjs/next.config.js b/utils/build/docker/nodejs/nextjs/next.config.js index 86e5f4cc8c6..c892b7d32b6 100644 --- a/utils/build/docker/nodejs/nextjs/next.config.js +++ b/utils/build/docker/nodejs/nextjs/next.config.js @@ -3,7 +3,11 @@ const nextConfig = { // Disabled because standalone mode does not support using modules directly // from node_modules which is necessary when using `npm link dd-trace`. // output: 'standalone' - outputFileTracing: false + outputFileTracing: false, + experimental: { + // Run out src/instrumentation.js hooks + instrumentationHook: true + } } module.exports = nextConfig diff --git a/utils/build/docker/nodejs/nextjs/src/instrumentation.js b/utils/build/docker/nodejs/nextjs/src/instrumentation.js new file mode 100644 index 00000000000..dfdfa6d557a --- /dev/null +++ b/utils/build/docker/nodejs/nextjs/src/instrumentation.js @@ -0,0 +1,33 @@ +'use strict' + +// Time to wait after invoking dd-trace's beforeExit handlers before forcing +// the process to exit. The handlers are fire-and-forget (no Promise, no +// awaitable signal) and initiate asynchronous HTTP exports for telemetry, +// traces, remote config, AppSec metrics, dogstatsd, etc., so we need a grace +// window for those in-flight requests to drain. Same as docker stop's default +// 10s SIGKILL timeout. +const SHUTDOWN_GRACE_MS = 10000 + +function shutdown () { + const ddTrace = globalThis[Symbol.for('dd-trace')] + + if (ddTrace?.beforeExitHandlers) { + for (const handler of ddTrace.beforeExitHandlers) { + try { + handler() + } catch (err) { + // Best-effort: keep running other handlers even if one throws. + console.error('dd-trace beforeExit handler threw', err) + } + } + } + + setTimeout(() => process.exit(0), SHUTDOWN_GRACE_MS).unref() +} + +export function register () { + if (!process.env.NEXT_MANUAL_SIG_HANDLE || process.env.NEXT_RUNTIME === 'edge') return + + process.once('SIGINT', shutdown) + process.once('SIGTERM', shutdown) +}