Skip to main content
Lifecycle scripts run at specific points during package installation and other operations.

Available scripts

Installation scripts

Run during bun install:
  • preinstall - Before package is installed
  • install - After package is installed
  • postinstall - After package and its dependencies are installed
  • prepare - After package is installed and before publishing
  • preprepare - Before prepare
  • postprepare - After prepare

Publishing scripts

Run during bun publish:
  • prepublishOnly - Before package is published
  • prepack - Before tarball is created
  • postpack - After tarball is created
  • publish - After package is published
  • postpublish - After package is published

Other scripts

Run during specific commands:
  • preuninstall - Before package is removed
  • uninstall - When package is removed
  • postuninstall - After package is removed

Defining scripts

Define lifecycle scripts in package.json:
{
  "scripts": {
    "preinstall": "echo 'About to install'",
    "install": "node-gyp rebuild",
    "postinstall": "bun run build",
    "prepare": "bun run build"
  }
}

Execution order

During bun install

For each package:
  1. preinstall
  2. Install package files
  3. install / postinstall (of dependencies)
  4. postinstall (of current package)
  5. preprepare / prepare / postprepare

During bun publish

  1. prepare
  2. prepublishOnly
  3. prepack
  4. Create tarball
  5. postpack
  6. Upload to registry
  7. publish
  8. postpublish

Common use cases

Build native modules

{
  "scripts": {
    "install": "node-gyp rebuild"
  }
}

Build TypeScript

{
  "scripts": {
    "prepare": "tsc"
  }
}

Download binaries

{
  "scripts": {
    "postinstall": "node scripts/download-binary.js"
  }
}

Generate files

{
  "scripts": {
    "postinstall": "bun run codegen"
  }
}

Setup database

{
  "scripts": {
    "postinstall": "bun run db:migrate"
  }
}

Disabling scripts

Skip all lifecycle scripts

bun install --ignore-scripts

Via bunfig.toml

[install]
ignoreScripts = true

Skip specific packages

Bun skips scripts for untrusted packages by default.

Trusted dependencies

By default, Bun only runs scripts for trusted packages.

Default trusted packages

Bun trusts these packages by default:
  • esbuild
  • puppeteer
  • sharp
  • msgpackr-extract
  • @biomejs/biome
  • Many more (see bun pm default-trusted)

View default trusted list

bun pm default-trusted

Trust specific packages

Add to trustedDependencies in package.json:
{
  "trustedDependencies": [
    "my-package",
    "@myorg/another-package"
  ]
}

Trust during install

bun install --trust my-package
Or trust all:
bun install --trust

View untrusted dependencies

$ bun pm untrusted
The following dependencies have install scripts but are not trusted:
  - [email protected]
  - [email protected]

To trust them, run:
  bun pm trust puppeteer electron

Trust untrusted dependencies

bun pm trust puppeteer electron
Or trust all:
bun pm trust --all

Script environment

Environment variables

Bun sets these variables:
  • npm_package_name - Package name
  • npm_package_version - Package version
  • npm_lifecycle_event - Current script name
  • npm_lifecycle_script - Current script command
  • INIT_CWD - Original working directory

Example

{
  "name": "my-package",
  "version": "1.0.0",
  "scripts": {
    "postinstall": "echo $npm_package_name@$npm_package_version"
  }
}
Output:

Parallel execution

Bun runs scripts in parallel when possible.

Set concurrency

bun install --concurrent-scripts 4

Via bunfig.toml

[install]
concurrentScripts = 4
Default: Number of CPU cores

Exit codes

Success

Script exits with code 0:
{
  "scripts": {
    "postinstall": "echo 'Done'"
  }
}

Failure

Script exits with non-zero code:
{
  "scripts": {
    "postinstall": "exit 1"
  }
}
Bun fails the installation.

Ignore failures

Prefix command with - to ignore failures:
{
  "scripts": {
    "postinstall": "-command-that-might-fail"
  }
}

Shell

Bun uses the system shell to run scripts:
  • Unix: /bin/sh
  • Windows: cmd.exe

Shell commands

{
  "scripts": {
    "postinstall": "echo 'Hello' && ls -la"
  }
}

Node scripts

{
  "scripts": {
    "postinstall": "node scripts/setup.js"
  }
}

Bun scripts

{
  "scripts": {
    "postinstall": "bun run scripts/setup.ts"
  }
}

Workspace scripts

In monorepos, lifecycle scripts run for each workspace package.

Root scripts

Run after all workspace packages:
{
  "workspaces": ["packages/*"],
  "scripts": {
    "postinstall": "echo 'All packages installed'"
  }
}

Workspace package scripts

Run for each workspace:
// packages/app/package.json
{
  "scripts": {
    "postinstall": "bun run build"
  }
}

Best practices

Keep scripts fast

Slow scripts delay installations:
{
  "scripts": {
    // Bad: Slow compilation
    "postinstall": "npm run build:full",
    
    // Good: Fast setup
    "postinstall": "node scripts/quick-setup.js"
  }
}

Use prepare for publishing

Build before publishing:
{
  "scripts": {
    "prepare": "bun run build"
  }
}
Runs before bun publish and after bun install.

Don’t use postinstall for building

Avoid building in postinstall for packages:
// Bad (in published package)
{
  "scripts": {
    "postinstall": "tsc"
  }
}

// Good (build before publishing)
{
  "scripts": {
    "prepare": "tsc",
    "prepublishOnly": "bun test"
  },
  "files": ["dist"]
}

Check for required tools

{
  "scripts": {
    "preinstall": "node -v && bun -v"
  }
}

Use .bunignore

Prevent scripts in development:
# .bunignore
node_modules/

Debugging

Verbose output

bun install --verbose
Shows script execution:
Running postinstall script for my-package...
> bun run build

Build complete

View script timing

Bun logs slow scripts (>500ms):
postinstall script for my-package took 1.2s

Debug script failures

If a script fails:
$ bun install

Error running postinstall for my-package:
  Command failed: exit code 1
  
Run with --verbose for more details
Debug:
bun install --verbose

Examples

Build TypeScript package

{
  "name": "my-lib",
  "scripts": {
    "prepare": "tsc",
    "prepublishOnly": "bun test"
  },
  "files": ["dist"],
  "main": "dist/index.js",
  "types": "dist/index.d.ts"
}

Download platform binary

{
  "scripts": {
    "postinstall": "node scripts/download-binary.js"
  }
}
scripts/download-binary.js:
const os = require('os');
const fs = require('fs');
const https = require('https');

const platform = os.platform();
const arch = os.arch();
const url = `https://example.com/binary-${platform}-${arch}`;

const file = fs.createWriteStream('bin/binary');
https.get(url, (response) => {
  response.pipe(file);
  file.on('finish', () => {
    fs.chmodSync('bin/binary', 0o755);
  });
});

Native module compilation

{
  "scripts": {
    "install": "node-gyp rebuild"
  },
  "devDependencies": {
    "node-gyp": "^10.0.0"
  }
}

Generate code

{
  "scripts": {
    "postinstall": "bun run generate",
    "generate": "bun run scripts/codegen.ts"
  }
}

Setup development environment

{
  "scripts": {
    "postinstall": "bun run setup",
    "setup": "bun run db:migrate && bun run seed"
  }
}

Build docs developers (and LLMs) love