---
title: "bun publish"
description: Use `bun publish` to publish a package to the npm registry
---

import Publish from "/snippets/cli/publish.mdx";

`bun publish` packs your package into a tarball and strips catalog and workspace protocols from the `package.json`, resolving versions if necessary. It then publishes to the registry specified in your configuration files or in the package's `publishConfig`. Both `bunfig.toml` and `.npmrc` files are supported.

```sh terminal icon="terminal"
## Publishing the package from the current working directory
bun publish
```

```txt
bun publish v1.3.3 (ca7428e9)

packed 203B package.json
packed 224B README.md
packed 30B index.ts
packed 0.64KB tsconfig.json

Total files: 4
Shasum: 79e2b4377b63f4de38dc7ea6e5e9dbee08311a69
Integrity: sha512-6QSNlDdSwyG/+[...]X6wXHriDWr6fA==
Unpacked size: 1.1KB
Packed size: 0.76KB
Tag: latest
Access: default
Registry: http://localhost:4873/

 + publish-1@1.0.0
```

To pack and publish separately, run `bun pm pack`, then `bun publish` with the path to the output tarball.

```sh terminal icon="terminal"
bun pm pack
...
bun publish ./package.tgz
```

<Note>
  `bun publish` does not run lifecycle scripts (`prepublishOnly/prepack/prepare/postpack/publish/postpublish`) if you
  provide a tarball path. Scripts run only when `bun publish` packs the package itself.
</Note>

<Note>
  `bun publish` cannot generate provenance statements yet. `--provenance`, `"provenance": true` in `publishConfig` and
  `NPM_CONFIG_PROVENANCE=true` are errors.
</Note>

### `--recursive` and `--filter`

In a monorepo, `--recursive` (`-r`) publishes the root package and every workspace package, dependencies before their dependents. `--filter` (`-F`) does the same for the matching workspace packages only.

```sh terminal icon="terminal"
bun publish --recursive
bun publish --filter "@acme/*"
```

Packages that are `"private"` or whose version is already on the registry are skipped. Every package is packed before the first one is published, and the first failure ends the run; running the command again skips what was published.

### `--access`

`--access` sets the access level of the package being published, either `public` or `restricted`. Unscoped packages are always public, and publishing an unscoped package with `--access restricted` is an error.

```sh terminal icon="terminal"
bun publish --access public
```

You can also set `--access` in the `publishConfig` field of your `package.json`.

```json package.json icon="file-json"
{
  "publishConfig": {
    "access": "restricted"
  }
}
```

### `--tag`

Set the tag of the package version being published. By default, the tag is `latest`. The initial version of a package is always given the `latest` tag in addition to the specified tag.

```sh terminal icon="terminal"
bun publish --tag alpha
```

You can also set `--tag` in the `publishConfig` field of your `package.json`.

```json package.json icon="file-json"
{
  "publishConfig": {
    "tag": "next"
  }
}
```

A prerelease version such as `2.0.0-beta.1` needs a tag: without one it would become `latest`, the version `bun add` installs. To publish it as `latest` anyway, pass `--tag latest`.

A tag cannot be a version or a version range, such as `1.2.3` or `v1`.

### `publishConfig.registry`

A package can pin the registry it is published to in the `publishConfig` field of its `package.json`, regardless of the registry configured for installs. `--registry` on the command line still overrides it.

```json package.json icon="file-json"
{
  "name": "internal-package",
  "publishConfig": {
    "registry": "https://registry.myorg.example/"
  }
}
```

As in npm, `publishConfig.registry` replaces the default registry only. A registry configured for the package's scope (`@myorg:registry` in `.npmrc`, or `[install.scopes]` in `bunfig.toml`) still takes precedence over it; to replace that one, set the scope's key in `publishConfig` instead:

```json package.json icon="file-json"
{
  "name": "@myorg/package",
  "publishConfig": {
    "@myorg:registry": "https://registry.myorg.example/"
  }
}
```

Credentials are the ones configured for that registry, the same as when it is the default registry: a `//registry.myorg.example/:_authToken=...` line in `.npmrc`, or a registry with the same URL in `bunfig.toml` or `.npmrc`. When there are none, the credentials of the registry the package would otherwise have been published to are used only if the pinned registry has the same origin; otherwise `bun publish` fails with a missing authentication error instead of sending them to a different registry.

As in npm, `publishConfig` is read like the name and the version: from the `package.json` that the `prepublishOnly`, `prepack` and `prepare` scripts leave behind.

### `--dry-run`

`--dry-run` runs the publish process without publishing the package, so you can verify what would be published.

```sh terminal icon="terminal"
bun publish --dry-run
```

### `--tolerate-republish`

Exit with code 0 instead of 1 if the package version already exists. Useful in CI/CD where jobs may be re-run.

```sh terminal icon="terminal"
bun publish --tolerate-republish
```

### `--gzip-level`

Set the gzip compression level used when packing the package, from `0` to `9` (default `9`). Only applies to `bun publish` without a tarball path argument.

### `--auth-type`

If you have 2FA enabled for your npm account, `bun publish` prompts you for a one-time password, either through a browser or in the CLI. `--auth-type` tells the npm registry which method you prefer: `web` (the default) or `legacy`.

```sh terminal icon="terminal"
bun publish --auth-type legacy
...
This operation requires a one-time password.
Enter OTP: 123456
...
```

### `--otp`

Provide a one-time password directly to the CLI. If the password is valid, `bun publish` skips the extra one-time password prompt before publishing:

```sh terminal icon="terminal"
bun publish --otp 123456
```

<Note>
  `bun publish` respects the `NPM_CONFIG_TOKEN` environment variable, useful when publishing from GitHub Actions or
  other automated workflows.
</Note>

### `--provenance`

`bun publish` cannot generate provenance statements yet: `--provenance`, `--provenance-file`, `"provenance": true` in `publishConfig`, `NPM_CONFIG_PROVENANCE=true` and `provenance=true` in `.npmrc` are errors, so that a package is never published without the statement it asked for.

A setting that turns provenance off is fine. As in npm, the command line wins over `publishConfig`, `publishConfig` over the environment, and the environment over `.npmrc`:

```sh terminal icon="terminal"
bun publish --no-provenance # or --provenance=false
```

---

<Publish />
