next-bun-compile
v0.5.1
Published
Next.js Build Adapter that compiles your app into a Bun single-file executable
Downloads
1,256
Maintainers
Readme
next-bun-compile
A Next.js Build Adapter that compiles your app into a single-file Bun executable.
One command. One binary. No runtime dependencies.
next build # → ./server (single executable with embedded assets)Requirements
Installation
bun add -D next-bun-compileSetup
Add the adapter to your next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
adapterPath: require.resolve("next-bun-compile"),
},
};
export default nextConfig;Update your build script in package.json:
{
"scripts": {
"build": "next build && next-bun-compile"
}
}Usage
bun run build # Builds Next.js + compiles to ./server
./server # Starts on port 3000The binary is fully self-contained — static assets, public files, and the Next.js server are all embedded. Just copy it anywhere and run.
Cross-Compilation
Any flags passed to next-bun-compile are forwarded to bun build --compile. Use --target to cross-compile for a different platform:
next-bun-compile --target=bun-linux-x64
next-bun-compile --target=bun-linux-arm64
next-bun-compile --target=bun-windows-x64See the Bun cross-compilation docs for all available targets.
Environment Variables
| Variable | Default | Description |
|---|---|---|
| PORT | 3000 | Server port |
| HOSTNAME | 0.0.0.0 | Server hostname |
| KEEP_ALIVE_TIMEOUT | — | HTTP keep-alive timeout (ms) |
CDN / assetPrefix
If you configure assetPrefix in your next.config.ts, static assets (/_next/static/) are served from your CDN instead of the origin server. The adapter detects this automatically and skips embedding static assets in the binary — only public files are embedded. This results in a smaller binary.
const nextConfig: NextConfig = {
assetPrefix: "https://cdn.example.com",
experimental: {
adapterPath: require.resolve("next-bun-compile"),
},
};You'll need to upload .next/static/ to your CDN separately.
Performance
The compiled binary uses Bun's --bytecode flag to pre-compile JavaScript to bytecode at build time, skipping the parsing step at startup. Code is also minified and dead code paths (dev-only modules, non-turbo runtimes) are eliminated via --define flags.
Benchmarks on a real Next.js app (both running on Bun's runtime):
| | Standalone | Compiled Binary | |---|---|---| | Startup | 84ms | 45ms (1.9x faster) | | Memory (RSS) | 60 MB | 72 MB | | Size | 91 MB (directory) | 99 MB (single file) |
Startup improvements scale with codebase size — larger applications benefit more since there's more code to parse.
How It Works
- Adapter hook —
modifyConfig()setsoutput: "standalone"automatically so you don't need to configure it - Asset discovery — Scans
.next/static/andpublic/for all static files - Code generation — Creates a
server-entry.jsthat:- Embeds all assets into the binary via Bun's
import ... with { type: "file" } - Extracts them to disk on first run
- Fixes
__dirnamefor compiled binary context - Starts the Next.js server
- Embeds all assets into the binary via Bun's
- Compilation — Runs
bun build --compilewith--defineflags to eliminate dead code branches (dev-only modules, non-turbo runtimes)
Module Stubs
Some modules can't be resolved at compile time but are never reached in production (dev servers, optional dependencies). The adapter creates no-op stubs for these only if the real module isn't installed. If you actually use @opentelemetry/api or critters, the real package gets bundled instead.
Troubleshooting
Packages with dynamic require() calls (e.g. pino)
Some packages like pino use dynamic require() calls internally (for worker threads, transports, etc.). Turbopack can't resolve these at build time, so they fail at runtime inside the compiled binary with errors like:
Failed to load external module pino-142500b1eb3f4baf: Cannot find package ...Fix: Add the package to transpilePackages in your next.config.ts:
const nextConfig: NextConfig = {
transpilePackages: ["pino", "pino-pretty"],
experimental: {
adapterPath: require.resolve("next-bun-compile"),
},
};This forces Turbopack to fully compile the package source rather than deferring dynamic requires to runtime.
Support
If this saved you time, consider supporting the project:
License
MIT
