bun publish

Use bun publish to publish a package to the npm registry

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.

terminal
## Publishing the package from the current working directory
bun publish
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.

terminal
bun pm pack
...
bun publish ./package.tgz

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.

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

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

terminal
bun publish --recursive
bun publish --filter "@acme/*"

Both flags must come after publish. bun --filter "@acme/*" publish means something else: it runs each matching package's publish script, like any other --filter command.

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.

terminal
bun publish --access public

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

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

terminal
bun publish --tag alpha

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

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

package.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:

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

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.

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.

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:

terminal
bun publish --otp 123456

bun publish respects the NPM_CONFIG_TOKEN environment variable, useful when publishing from GitHub Actions or other automated workflows.

--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:

terminal
bun publish --no-provenance # or --provenance=false

CLI Usage#

terminal
bun publish dist

Publishing Options#

--accessstring

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

terminal
bun publish --access public

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

package.json
{
  "publishConfig": {
    "access": "restricted" 
  }
}
--tagstringdefault:latest

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

terminal
bun publish --tag alpha

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

package.json
{
  "publishConfig": {
    "tag": "next" 
  }
}
--dry-runboolean

Simulate the publish process without publishing the package, to verify its contents first.

bun publish --dry-run
--tolerate-republishboolean

bun publish exits with code 0 instead of 1 when the version being published already exists in the registry.

--recursiveboolean

Publish the root package and every workspace package, dependencies before their dependents. Alias: -r

A package is skipped when it is "private" or when its version is already on the registry. 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.

terminal
bun publish --recursive
--filterstring

Like --recursive, for the workspace packages that match the pattern only. Can be repeated. Alias: -F

terminal
bun publish --filter "@acme/*"
--gzip-levelstringdefault:9

Specify the level of gzip compression to use when packing the package. Only applies to bun publish without a tarball path argument. Values range from 0 to 9 (default is 9).

--auth-typestringdefault:web

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

terminal
bun publish --auth-type legacy
...
This operation requires a one-time password.
Enter OTP: 123456
...
--otpstring

Provide a one-time password directly to the CLI. A valid password skips the extra one-time password prompt before publishing.

terminal
bun publish --otp 123456

bun publish respects the NPM_CONFIG_TOKEN environment variable, so you can publish from GitHub Actions or other automated workflows.

Registry Configuration#

Custom Registry#

--registrystring

Use a specific registry by default, overriding .npmrc, bunfig.toml and environment variables. A registry configured for the package's scope (@scope:registry= in .npmrc or [install.scopes] in bunfig.toml) still takes precedence.

bun publish --registry https://my-private-registry.com

A package can pin the registry it is published to with publishConfig.registry in its package.json (--registry still overrides it). Like npm, this replaces the default registry only; a registry configured for the package's scope takes precedence unless publishConfig also sets @scope:registry.

package.json
{
  "publishConfig": {
    "registry": "https://my-private-registry.com" 
  }
}

SSL Certificates#

--castring

Provide Certificate Authority signing certificate

--cafilestring

Path to Certificate Authority certificate file

--use-system-caboolean

Use the system's trusted certificate authorities

bun publish --ca "-----BEGIN CERTIFICATE-----..."

General Options#

Dependency Management#

-p, --productionboolean

Don't install devDependencies

--omitstring

Exclude dependency types: dev, optional, or peer

-f, --forceboolean

Always request the latest versions from the registry & reinstall all dependencies

Script Control#

--ignore-scriptsboolean

Skip lifecycle scripts during packing and publishing

--trustboolean

Add packages to trustedDependencies and run their scripts

Lifecycle Scripts — When you publish a pre-built tarball, Bun does not run lifecycle scripts such as prepublishOnly and prepack; they only run when Bun packs the package itself.

File Management#

--no-saveboolean

Don't update package.json or lockfile

--frozen-lockfileboolean

Disallow changes to lockfile

--yarnboolean

Generate yarn.lock file (yarn v1 compatible)

Performance#

--backendstring

Platform optimizations: clonefile (default on macOS), hardlink (default on Linux and Windows), symlink, or copyfile

--network-concurrencynumberdefault:64

Maximum concurrent network requests

--concurrent-scriptsnumber

Maximum concurrent lifecycle scripts (default: 2x CPU cores)

Output Control#

--silentboolean

Suppress all output

--verboseboolean

Show detailed logging

--no-progressboolean

Hide progress bar

--no-summaryboolean

Don't print publish summary