/flags/bun/bun-install-production
bun install --production: skip devDependencies
- tool
- Bun
- command
- bun install
- flag
- --production, -p
- default
- off (dependencies and devDependencies are installed)
bun install --production installs only dependencies (and optionalDependencies), skipping everything in devDependencies. Use it in Docker images and production servers where test runners, type checkers and bundlers are not needed. Combine it with --frozen-lockfile so the install fails instead of resolving new versions when bun.lock and package.json disagree.
With and without --production
is-number is a runtime dependency, is-odd a dev dependency:
cat > package.json <<'EOF'
{
"name": "demo",
"dependencies": { "is-number": "7.0.0" },
"devDependencies": { "is-odd": "3.0.1" }
}
EOF
npx --yes bun@1 install >/dev/null 2>&1
echo "--- bun install"
ls node_modules
rm -rf node_modules
npx --yes bun@1 install --production >/dev/null 2>&1
echo "--- bun install --production"
ls node_modules
echo "--- --production --frozen-lockfile after a package.json change"
sed -i 's/"is-number": "7.0.0"/"is-number": "6.0.0"/' package.json
npx --yes bun@1 install --production --frozen-lockfile 2>&1 | grep -m1 "^error"Output
--- bun install is-number is-odd --- bun install --production is-number --- --production --frozen-lockfile after a package.json change error: lockfile had changes, but lockfile is frozen
The full install brings is-odd as well; with --production only is-number is in node_modules. When package.json changed but bun.lock did not, --frozen-lockfile stops with lockfile had changes, but lockfile is frozen.
Related install options
| Option | Effect |
|---|---|
| --production, -p | Do not install devDependencies |
| --omit=dev | Same effect, npm-style; also accepts optional and peer |
| --frozen-lockfile | Fail if bun.lock would change (use in CI and Docker) |
| --no-save | Install without updating bun.lock or package.json |
| --dry-run | Show what would be installed without installing |
| install.production = true in bunfig.toml | Make --production the default for this project |
In a Dockerfile
FROM oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --production --frozen-lockfile
FROM oven/bun:1
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
CMD ["bun", "src/index.ts"]If the image also builds the app (Vite, bun build, TypeScript compilation), run a full bun install and the build in one stage, and the production install in another, because the build tools live in devDependencies.
Pitfalls
- A runtime import that is listed under
devDependenciesworks locally and fails in production with Cannot find module. Move it withbun add pkg. - Start from an empty
node_modules, as in a fresh Docker layer, so nothing from an earlier full install is copied into the image by accident. - Lifecycle scripts of the remaining packages still follow Bun's trusted-dependencies rules;
--productiondoes not change them.
FAQ
How do I install only production dependencies with Bun in Docker?
Copy package.json and bun.lock, then run bun install --production --frozen-lockfile. devDependencies are skipped and the build fails if the lockfile does not match package.json.
Is bun install --production the same as npm ci --omit=dev?
Close. Both skip devDependencies. npm ci also deletes node_modules and requires a lockfile; Bun needs --frozen-lockfile for the lockfile rule and installs into the existing node_modules.
Why do I get Cannot find module after bun install --production?
A package the app imports at runtime is listed under devDependencies. Move it to dependencies (bun add pkg) or build before pruning; build tools such as TypeScript or Vite are needed only at build time.