# Docker Build Fix: Next.js 16 + Turbopack + better-sqlite3 ## Problem The original multi-stage Dockerfile failed to build because Next.js 16's Turbopack bundler doesn't properly externalize native Node modules during the "collecting page data" build phase, even with `serverExternalPackages` configured. ## Root Causes & Fixes ### 1. Missing build tools `node:20-slim` lacks the toolchain for compiling native addons. **Fix:** Install `python3 make g++` in the base image. ### 2. pnpm skipping native compilation `better-sqlite3` was missing from `pnpm.onlyBuiltDependencies` in package.json, so pnpm silently skipped its build script. **Fix:** Add `"better-sqlite3"` alongside `"bcrypt"` in the array. ### 3. Turbopack native addon resolution Turbopack rewrites `__dirname` in bundled server code, so the `bindings` package can't locate `better_sqlite3.node` via its normal path resolution. **Fix:** Copy the compiled `.node` file from `build/Release/` to `build/` where the bindings resolver checks first. ### 4. Eager database initialization at build time `db.ts` exports `const db = createDb()` which runs immediately on import. Since the root layout imports `db`, the build phase tries to open a real SQLite connection. **Fix:** Create `/app/data` directory and set `DATABASE_PATH` during the build, then clean up the build-time database before the runner stage. ### 5. Multi-stage COPY losing native addon The original 3-stage Dockerfile (base → deps → builder → runner) copied `node_modules` between stages, but the native `.node` binary wasn't reliably preserved. **Fix:** Collapse to a single build stage, then copy only the standalone output to a clean runner stage. ## Final Dockerfile Structure ``` Stage 0 (base): Install tools -> pnpm install -> copy addon -> mkdir data -> pnpm build -> rm data Stage 1 (runner): node:20-slim -> copy standalone + static + public -> run as nextjs user ```
Summary of fixes needed to build a Next.js 16 app with better-sqlite3 native addon in Docker with Turbopack
Log in to leave a comment.