Skip to content

/flags/bun/bun-install-production

bun install --production: skip devDependencies

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
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:

bash
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.

OptionEffect
--production, -pDo not install devDependencies
--omit=devSame effect, npm-style; also accepts optional and peer
--frozen-lockfileFail if bun.lock would change (use in CI and Docker)
--no-saveInstall without updating bun.lock or package.json
--dry-runShow what would be installed without installing
install.production = true in bunfig.tomlMake --production the default for this project

In a Dockerfile

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 devDependencies works locally and fails in production with Cannot find module. Move it with bun 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; --production does 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.

Sources