---
title: "Auto-install"
description: "Bun's automatic package installation feature for standalone script execution"
---

If Bun finds no `node_modules` directory in the working directory or higher, it abandons Node.js-style module resolution in favor of the **Bun module resolution algorithm**.

Under Bun-style module resolution, Bun auto-installs every imported package on the fly into a [global module cache](/pm/global-cache) during execution. [`bun install`](/pm/cli/install) uses the same cache.

```ts index.ts icon="/icons/typescript.svg"
import { foo } from "foo"; // install `latest` version

foo();
```

The first time you run this script, Bun auto-installs `"foo"` and caches it. Later runs use the cached version.

Auto-install is off when a [security scanner](/runtime/bunfig#installsecurityscanner) is configured in the global `.bunfig.toml`, or in a `bunfig.toml` from the working directory or the importing file up to the [project root](#project-root). It is also off when one of those files, a `.npmrc`, or a `package.json` that Bun reads to find the project root cannot be read or parsed. `-i` and `--install` do not turn it back on. The import fails and tells you to run `bun install`.

---

## Project root

Auto-install reads the lockfile and the settings of one project, the project of the importing file:

1. The nearest directory, at or above the file, that has a `package.json` with a `name`, `workspaces`, or dependencies. A `package.json` without any of them, such as `{ "type": "commonjs" }`, belongs to the project above it.
2. The workspace root, if that directory is a member of a workspace.
3. The topmost directory that has a `package.json`, if none of them has a `name`, `workspaces`, or dependencies.
4. The directory of the file, if there is no `package.json`.

A file in `node_modules` belongs to the project that the `node_modules` directory is in. Bun reads the lockfile of the project root only. It reads a `.npmrc` and the `[install]` settings of a `bunfig.toml` in each directory from the project root down to the importing file, and in the directory where you started `bun`.

The first import that needs auto-install decides the project for the rest of the process.

---

## Version resolution

Bun determines which version to install as follows:

1. Scan up the tree for a `package.json` that lists `"foo"` in `dependencies`, `devDependencies`, `optionalDependencies`, or `peerDependencies`. If found, use the specified semver version or version range.
2. Otherwise, use `latest`.
3. Look for a `bun.lock` file in the [project root](#project-root). If the lockfile has a version of `"foo"` that satisfies the range, use that version.

The dependencies of a package that comes from the lockfile also load the versions in the lockfile.

A `workspace:`, `file:`, or `link:` dependency loads from its folder on disk, and so does a workspace package that a range matches. Nothing is copied. A tarball (a `file:` path or a URL) is extracted into the cache.

Without a lockfile, Bun reads `overrides`, `resolutions`, `catalog`, `catalogs`, and `workspaces` from the `package.json` of the project root. A peer dependency of an auto-installed package loads the version that this `package.json` lists, if it lists one.

---

## Cache behavior

Once Bun determines a version or version range, it:

1. Checks the module cache for a compatible version. If one exists, uses it.
2. When resolving `latest`, checks if `package@latest` was downloaded and cached in the last _24 hours_. If so, uses it.
3. Otherwise, downloads and installs the appropriate version from the registry.

With `bun --prefer-offline` (or [`install.prefer = "offline"`](/runtime/bunfig#installprefer)), Bun resolves versions and tags from the cached registry metadata, however old it is, and only asks the registry for a package that is not in the cache.

The first import of a package that is not in the cache blocks the thread until the download finishes. Timers and other requests' handlers do not run inside `require()`, `import()`, or `Bun.resolveSync()`.

The import fails if the registry sends nothing for about 30 seconds (`BUN_CONFIG_HTTP_IDLE_TIMEOUT` changes that).

When the registry answers that a package does not exist (404), Bun does not ask for it again for 5 minutes, or less if the response's `Cache-Control` says so. After any other failure of a lookup or a download, such as a 5xx or a dropped connection, Bun retries the request a few times. If the import still fails, an import in a later turn of the event loop (after a timer or I/O) asks again.

When Bun cannot write to the cache, on a read-only file system for example, packages that are already in the cache still load. An import that needs a download throws the same error as an import of a missing package, with the reason as a note.

---

## Registries

Bun downloads packages from the npm registry (`https://registry.npmjs.org/`) unless you configure another one. Auto-install reads the default registry, the scoped registries, and their credentials from [`bunfig.toml`](/runtime/bunfig#installregistry) and from [`.npmrc`](/pm/npmrc).

```ini .npmrc icon="npm"
@myorg:registry=https://npm.myorg.com/
//npm.myorg.com/:_authToken=${NPM_TOKEN}
```

Bun reads the user's `.npmrc`, then the `.npmrc` in the directory where you started `bun`, then each `.npmrc` from the [project root](#project-root) down to the importing file, then the `[install]` settings of each `bunfig.toml` in those same directories, then the `bunfig.toml` in the directory where you started `bun`. A later file overrides the same value in an earlier one. Other `.npmrc` options, such as `cache`, apply to `bun install` and the other package manager commands only. `install.auto` and `install.prefer` count only in the directory where you started `bun`.

Auto-install replaces `${NAME}` with a variable from the environment of the script, which includes its `.env` files. `bun install` reads `.env.production` where a script reads `.env.development` by default.

---

## Installation

Bun installs and caches packages into `<cache>/<pkg>@<version>`, so Bun can cache multiple versions of the same package at once. It also creates a symlink under `<cache>/<pkg>/<version>` to speed up looking up all cached versions of a package.

A package whose tarball ships a `node_modules` folder (`bundledDependencies`) loads what is in that folder from there, and its other dependencies from the cache.

---

## Version specifiers

To bypass version resolution entirely, specify a version or version range directly in your import statement.

```ts index.ts icon="/icons/typescript.svg"
import { z } from "zod@3.0.0"; // specific version
import { z } from "zod@next"; // npm tag
import { z } from "zod@^3.20.0"; // semver range
```

---

## Benefits

- **Space efficiency** — Each version of a dependency exists in only one place on disk. This saves space and time compared to redundant per-project installations.
- **Portability** — Your source file is _self-contained_, so sharing scripts and gists doesn't mean zipping up a directory of code and config files. With version specifiers in `import` statements, even a `package.json` isn't necessary.
- **Convenience** — You don't need to run `npm install` or `bun install` before running a file or script with `bun run`.
- **Backwards compatibility** — Because Bun still respects the versions specified in `package.json` if one exists, you can switch to Bun-style resolution with a single command: `rm -rf node_modules`.

---

## Limitations

- No Intellisense. TypeScript auto-completion in IDEs relies on type declaration files inside `node_modules`. We are investigating solutions to this.
- No [patch-package](https://github.com/ds300/patch-package) support
- No git dependencies. Run `bun install` for a dependency on a git URL.
- An `overrides` or `resolutions` entry applies only if it names a version, a range, a tag, an `npm:` alias, or a `catalog:`. One that points at a folder (`file:`, `link:`), a tarball, or a git repository does not apply. Run `bun install` for those.
- One project per process. The registries, `overrides`, `resolutions`, and catalogs of another project do not apply to the files that the process imports from it.

---

## FAQ

<AccordionGroup>

    <Accordion title="How is this different from what pnpm does?">

    With pnpm, you have to run `pnpm install`, which creates a `node_modules` folder of symlinks for the runtime to resolve. By contrast, Bun resolves dependencies on the fly when you run a file; there's no need to run any `install` command ahead of time. Bun also doesn't create a `node_modules` folder.

    </Accordion>

    <Accordion title="How is this different from what Yarn Plug'N'Play does?">
    With Yarn, you must run `yarn install` before you run a script. By contrast, Bun resolves dependencies on the fly when you run a file; there's no need to run any `install` command ahead of time.

    Yarn Plug'N'Play also uses zip files to store dependencies. This makes dependency loading [slower at runtime](https://twitter.com/jarredsumner/status/1458207919636287490), as random access reads on zip files tend to be slower than the equivalent disk lookup.
    </Accordion>

    <Accordion title="How is this different from what Deno does?">

    Deno requires an `npm:` specifier before each npm `import`, lacks support for import maps through `compilerOptions.paths` in `tsconfig.json`, and has incomplete support for `package.json` settings. Unlike Deno, Bun does not currently support URL imports.
    </Accordion>

</AccordionGroup>
