> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/oven-sh/bun/llms.txt
> Use this file to discover all available pages before exploring further.

# Publishing Packages

> Publish packages to the npm registry with bun publish.

`bun publish` packs your package into a tarball and publishes it to the configured npm registry. It strips `workspace:` and `catalog:` version protocols from `package.json`, resolving them to real version numbers before publishing.

```bash theme={null}
bun publish
```

Example output:

```
bun publish v1.3.3

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: https://registry.npmjs.org/

 + my-package@1.0.0
```

## Authentication

Set an auth token via the `NPM_CONFIG_TOKEN` environment variable (suitable for GitHub Actions and other CI systems):

```bash theme={null}
NPM_CONFIG_TOKEN=my-token bun publish
```

Or configure the token in `bunfig.toml`:

```toml theme={null}
[install]
registry = { url = "https://registry.npmjs.org", token = "$NPM_CONFIG_TOKEN" }
```

Or in `.npmrc`:

```ini theme={null}
//registry.npmjs.org/:_authToken=${NPM_CONFIG_TOKEN}
```

## Flags

### `--access`

Set the access level of the published package. Required for scoped packages that should be publicly accessible:

```bash theme={null}
bun publish --access public
bun publish --access restricted
```

Unscoped packages are always public. You can also set access in `publishConfig` in `package.json`:

```json theme={null}
{
  "publishConfig": {
    "access": "public"
  }
}
```

### `--tag`

Publish under a specific dist-tag instead of `latest`:

```bash theme={null}
bun publish --tag beta
bun publish --tag next
```

Or in `package.json`:

```json theme={null}
{
  "publishConfig": {
    "tag": "next"
  }
}
```

### `--dry-run`

Simulate the publish without actually uploading anything. Shows you exactly which files would be included:

```bash theme={null}
bun publish --dry-run
```

### `--tolerate-republish`

Exit with code `0` instead of `1` if the package version already exists in the registry. Useful in CI jobs that may be re-run:

```bash theme={null}
bun publish --tolerate-republish
```

### `--otp`

Provide a one-time password for accounts with 2FA enabled:

```bash theme={null}
bun publish --otp 123456
```

### `--auth-type`

Choose how to complete 2FA: `web` (default) opens a browser, `legacy` prompts in the terminal:

```bash theme={null}
bun publish --auth-type legacy
```

## Controlling which files are published

Bun respects the `"files"` field in `package.json` and `.npmignore`. Only files listed in `"files"` (or not excluded by `.npmignore`) are included in the tarball.

```json theme={null}
{
  "name": "my-package",
  "version": "1.0.0",
  "files": [
    "dist",
    "index.js",
    "README.md"
  ]
}
```

## Pack without publishing

To create the tarball without uploading it, use `bun pm pack`:

```bash theme={null}
bun pm pack
# Creates my-package-1.0.0.tgz

# Save to a specific directory
bun pm pack --destination ./dist

# Preview contents without writing the file
bun pm pack --dry-run
```

You can then publish the tarball separately:

```bash theme={null}
bun publish ./my-package-1.0.0.tgz
```

<Note>
  When you pass a tarball path to `bun publish`, lifecycle scripts (`prepublishOnly`, `prepack`, `prepare`, `postpack`, `publish`, `postpublish`) are not run. Scripts only run when `bun publish` packs the package itself.
</Note>

## Lifecycle scripts during publish

When `bun publish` packs the package, it runs lifecycle scripts in this order:

1. `prepublishOnly`
2. `prepack`
3. `prepare`
4. *(pack)*
5. `postpack`
6. *(upload)*
7. `publish`
8. `postpublish`
