Observing NextJS in Production
April 2021
NextJS offers a great developer experience and impressive performance, but has a number of frustrating limitations that make it difficult to deploy in production: There's no way to plug in performance monitoring like Datadog APM, or customize error handling to use a reporting system like Sentry, and logs and errors are printed to STDOUT/STDERR unstructured with no way to customize them.
The strategies for hosting NextJS apps in production basically boil down to:
- Make your site totally static, so that there's nothing to worry about. Use the static host of your choice. Give up all API features, previewing, regeneration, etc.
- Deploy with Vercel, but give up all control over builds and hosting. No option to use Datadog APM or other dependencies that require an "agent".
- Deploy manually with
next start
, but be blind to state of the system in production. - Deploy manually and use a custom server entrypoint to add error handling and performance monitoring, but give up performance and features.
What we want
- A way to load Datadog APM
dd-trace
NodeJS package before importing NextJS, so that dd-trace can inject performance monitoring into lower level packages likehttp
. - A way to customizing logging so that logs can be parsed by our logging pipeline. Ideally, all logs would be JSON written to STDOUT or a UDP port.
Why not use a custom entrypoint?
The "escape hatch" is to use a "custom
server" entrypoint
instead of the recommended next start
command, but this has significant
drawbacks, as the docs warn:
Before deciding to use a custom server please keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like serverless functions and Automatic Static Optimization.
Yikes; the whole reason we're using NextJS is for Automatic Static Optimization, so this is unacceptable.
Using preloading
The NodeJS CLI has an option to require a node module before importing and
executing the entrypoint script: -r module / --require module
. If we put
our customization in a module that we preload before next start
, we can
work around many of NextJS's issues. We'll be able to use vanilla next start
self-hosting, but get to have Datadog APM, error reporting, and customized logging.
1. Create your preload
Create a server-preload.js
file in your NextJS project root. Here's a
lightly-edited version of Notion's server-preload.js. Below you'll see how we
set up Datadog tracing and fix the logging situation.
(Not seeing a big code block? Try refreshing, or click here to view the gist directly.)
2. Require your preload when you next start
Now you'll need to load your preload file when you start NextJS on the server.
Everywhere you have next start
, replace with:
node --require ./server-preload.js ./node_modules/.bin/next start
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Load our preload before running `next start`
You may beed to adjust paths to fit your app depending on your working directory where your app runs in production.
That's all there is to it!
Direct your questions/comments/concerns to @jitl on twitter.