npx checkly test or npx checkly deploy, the CLI packs your Playwright tests into a code bundle together with the files your package manager needs: package.json files, the lockfile, and .npmrc. Before each run, Checkly installs your dependencies from that bundle, fetching packages from the registries your project uses.
That install fails when your project depends on private packages from an internal registry that Checkly can’t reach, such as an Artifactory, Nexus, or Verdaccio instance behind your firewall or VPN, or an intranet-only mirror of the public npm registry. This page covers the CLI options for those setups.
If your private registry is reachable from the internet and only needs a token, such as GitHub Packages or a hosted npm organization, you don’t need to embed or route packages. Configure your
.npmrc as described in Private packages.
All of these options apply to Playwright Check Suites only, not to Browser Checks or Multistep Checks. You configure them at the top level of
checkly.config.ts, not per check.
Embed private packages
If some of your dependencies come from a private registry that Checkly’s infrastructure can’t reach, list them inbundle.packages.embed. The CLI downloads those packages on the machine where you run it, verifies each tarball against the integrity hash in your lockfile, and ships them inside the code bundle. The runner then installs them without contacting your registry.
checkly.config.ts
@acme scope that your lockfile contains, except @acme/public-sdk, which the runner must then fetch itself. If your .npmrc sends the whole @acme scope to your internal registry, route @acme/public-sdk to a registry the runner can reach with runner.registries. It also embeds version 2.1.0 of the unscoped legacy-private-pkg. Every other package installs from its usual registry.
Each entry is one of:
- A package name, such as
'@acme/private-utils'. Embeds every version of the package found in the lockfile. - An exact version pin, such as
'legacy-private-pkg@2.1.0'. Checkly lists only the embedded versions of an embedded package, so a pin must cover every version of that package in your lockfile. Otherwise the install can fail; npm, for example, can’t find the missing version.checkly testandcheckly deploywarn when an entry leaves versions of a package in your lockfile unembedded. Embed the package by name to include every version in your lockfile. - A name pattern, such as
'@acme/*', optionally prefixed with!to exclude packages that earlier entries selected.
Supported lockfiles
The CLI resolves entries against the lockfile at the workspace root:
Yarn Berry lockfiles don’t record npm tarball hashes, so for Yarn Berry the CLI looks them up in your registry’s package metadata each time it bundles your checks. Downloaded tarballs are still cached.
Validation
The CLI validates the list before deploying:- An entry that matches no package in the lockfile fails validation and blocks the deploy. Only list packages your project actually uses.
- An entry whose only matches can’t be embedded also fails validation. Packages installed from git, a local path, or a URL, and lockfile entries without an integrity hash, can’t be embedded; the runner must fetch them itself.
- Workspace members that a pattern matches are skipped, because they’re bundled as source code rather than embedded. An entry that matches only workspace members fails validation.
- A
!exclusion that matches nothing is ignored. - A list that ends up selecting no packages at all only prints a warning.
Registry credentials
Embedded packages need registry credentials only on the machine running the CLI, which downloads them. The runner installs them from the code bundle and never contacts your registry for them, so you don’t need to store your registry token in Checkly; a token reference in the bundled.npmrc whose variable isn’t set in Checkly doesn’t break the install. The CLI reads credentials from:
- The project, workspace root, and user
.npmrcfiles npm_config_*environment variables- pnpm’s global
auth.ini, wherepnpm loginstores tokens on pnpm 11 and later
//registry.example.com/:_authToken=${NPM_TOKEN}) rather than setting a bare _authToken. Because .npmrc is uploaded with the code bundle, reference tokens through environment variables instead of writing them in plain text, and set those variables wherever you run checkly test or checkly deploy. Only packages the runner fetches itself need their token stored in Checkly, as described in Private packages. If you use runner.registries, set those tokens in its auth entries instead.
If you use bun or Yarn Berry and your credentials only live in bunfig.toml or .yarnrc.yml, copy them into .npmrc or set them as npm_config_* variables; the CLI doesn’t read those files. bun and Yarn Berry projects must also keep their registry URLs, including scoped registries, in .npmrc.
If a download fails to authenticate, the error names the credential source the CLI used.
Code bundle size
The Checkly API accepts code bundles of up to 30 MB, compressed. Embedded packages ship inside the code bundle, so embedding large packages, or many packages that add up, can push the bundle over that limit.checkly test, checkly pw-test, and checkly deploy then fail when they upload the bundle:
Terminal
bundle.packages.prune drop the packages your checks don’t use. Packages that pruning removes from the bundled lockfile aren’t embedded.
Enterprise plans can raise the limit to 100 MB. Contact your account executive or contact us in the Checkly Web App.
Pruning
Pruning reduces what the runner installs to what your code bundle needs. This keeps internal packages your checks never use out of the install, so you don’t have to embed them or make their registry reachable. The CLI never modifies the files on disk; it only rewrites the copies in the code bundle.Lockfile pruning
In a monorepo, your code bundle often contains only some of the workspace members. Installing from the full lockfile would make the runner fetch dependencies of members that aren’t in the bundle, and fail if any of those come from a registry it can’t reach. The CLI therefore prunes the bundled lockfile to the bundle’s contents automatically. You don’t need to configure anything. Lockfile pruning supports the same lockfiles as embedding, and requires your package manager to be installed on the machine running the CLI. pnpm and npm reuse their local caches and only contact your registry for packages they haven’t cached; Yarn Berry and bun prune offline. If pruning is needed but can’t run or can’t verify its result, the CLI ships the original lockfile unchanged and prints a note explaining why. Embedding follows the pruned lockfile: a package that only workspace members outside the bundle depend on is not embedded, even if an entry matches it, because the runner never installs it. Control lockfile pruning with these environment variables:Prune manifest dependencies
Usebundle.packages.prune to remove dependencies from the package.json files in the code bundle: the workspace root’s and every bundled workspace member’s. The CLI rewrites the manifests before it prunes the lockfile, so the removed packages drop out of the bundled lockfile too. Besides the peer dependency case below, you can use it to speed up installs.
The most common use is unused peer dependencies. With pnpm’s auto-install-peers setting, which is on by default, a member’s peer dependencies end up in the lockfile even when your tests never import them. If those peers come from a registry Checkly can’t reach, the install fails:
checkly.config.ts
peerDependencies from the workspace root’s and each bundled member’s package.json. When the CLI then prunes the lockfile, the peers that were only installed to satisfy those entries drop out, so the runner doesn’t try to fetch them.
prune accepts two forms:
- Pattern list
- Per dependency class
An array of package name patterns. Matching packages are removed from every dependency class of every bundled This removes every
package.json:checkly.config.ts
@acme package except @acme/runtime, plus storybook, from dependencies, devDependencies, peerDependencies, and optionalDependencies.embed, prune patterns don’t accept version pins. A pattern that matches nothing is ignored. Removing a peer dependency also removes its peerDependenciesMeta entry. Patterns only match dependencies declared in package.json files; see Dependencies of dependencies.
Target specific workspace members
To prune only some workspace members, use member-scoped entries in the array form.member is a pattern list matched against each manifest’s name; use '.' for the workspace root. Each entry has exactly one of remove or keep, which take a pattern list or a per-class object:
checkly.config.ts
@acme/web and leaves every other member’s peers in place. The second entry reduces @acme/design-system to a single dependency, @acme/tokens: its other dependencies and all of its devDependencies, peerDependencies, and optionalDependencies are removed.
removeworks like the global patterns, but only for the matched members. Entries apply in the order you list them.keepdeclares a member’s entire remaining dependency set. Anything it doesn’t select is removed, classes a per-classkeepdoesn’t mention are emptied, and no other entry can remove a kept package.keep: []removes every dependency.
keep, since a wildcard can match a member you add later. The CLI warns when a member selector matches no workspace member, or when a keep pattern keeps nothing in a bundled member.
Requirements and caveats
- When the bundle contains a lockfile,
pruneonly takes effect if lockfile pruning succeeds in the same run, so the manifests and lockfile stay in sync. Otherwise the originalpackage.jsonfiles ship unchanged and the CLI prints a warning. A bundle without a lockfile always ships the pruned manifests. - The CLI doesn’t check your code against the pruned dependencies. Don’t remove anything your tests need at runtime, or anything the install itself needs. For example, keep
typescriptwhen a package’sprepareorpostinstallscript compiles TypeScript during the install (see lifecycle scripts).
Route runner installs to other registries
If your.npmrc points at a registry URL that only works on your network, such as a VPN-only mirror, but the same packages are available to Checkly from other registries, use runner.registries instead of embedding. It tells the runner which registries to install from, replacing the registries named in the bundled configuration files:
checkly.config.ts
@acme packages only from https://npm.acme.example.com/, authenticating with the ACME_NPM_TOKEN environment variable, and every other package from the public npm registry. If an @acme package is missing from the Acme registry, the install fails instead of asking the public registry for it.
upstreamsnames the registries the runner may use. Atokenmust be a single${VARIABLE}reference to a Checkly environment variable, which the runner resolves at install time. Literal tokens are rejected.- Once
runner.registriesis set, the runner ignores the registry credentials and TLS settings, such asstrict-sslandcafile, in your bundled.npmrc,pnpm-workspace.yaml, and.yarnrc.yml. Give every upstream that needs authentication its ownauthentry. packagesroutes package names to upstreams. The first rule whose pattern matches wins. The runner tries the rule’s upstreams in order, but never falls through to a later rule, so private package names aren’t sent to a broader registry. The last rule must be{ pattern: '**', ... }.- Routing patterns don’t accept
!exclusions or version pins. - Embedded packages never go through routing rules: the runner installs them from the code bundle and doesn’t ask any routed registry about them.
runner.registries only affects the install on Checkly. Embedding and lockfile pruning still run on your machine and use your .npmrc credentials.
Dependencies of dependencies
Your lockfile records every package the install needs, including dependencies of your dependencies (transitive dependencies). Each option handles them differently:Embed private dependency chains
The runner installs the dependencies of an embedded package like any other package, from the registries your project uses. That works for public dependencies, but if an embedded package depends on other private packages from the same unreachable registry, you must embed those too. The CLI doesn’t detect a missing one; the install on Checkly fails when it can’t fetch it. For example, if@acme/ui depends on the private @acme/tokens and the public react, embed both private packages and let the runner fetch react from npm:
checkly.config.ts
@acme/* embeds the whole scope at every depth, including private packages you only depend on indirectly.
Examples
Monorepo with an internal registry
Your tests live in one member of a pnpm workspace, and your.npmrc points the @acme scope at a registry that’s only reachable from your network:
design-system depends on the private @acme/tokens and declares the private @acme/analytics as a peer dependency, which the tests don’t use. admin depends on the private @acme/billing-sdk.
packages/e2e/checkly.config.ts
npx checkly deploy, the CLI:
- Bundles
e2eanddesign-system, because the tests importdesign-system.adminis left out. - Removes
@acme/analyticsfrom the bundled copy ofdesign-system’spackage.json. - Prunes the bundled lockfile to the bundle’s contents.
@acme/billing-sdkdrops out because onlyadminneeds it, and@acme/analyticsdrops out because nothing declares it anymore. - Embeds the
@acmepackages that remain in the pruned lockfile, here@acme/tokens, downloading them through your.npmrc. The pattern also matches the workspace members, such asdesign-system; those are bundled as source code and skipped.
@acme/tokens from the bundle and all public packages from npm. It never contacts your internal registry.
The prune entry isn’t required for the install to work: without it, @acme/* would also embed @acme/analytics. Pruning the unused peer saves downloading and shipping a package the tests never load. It becomes required when the runner can neither install the peer from the bundle nor fetch it itself, for example a peer installed from an internal Git server.
Private scope behind a VPN-only mirror
Your.npmrc sends every install through a mirror that’s only reachable on your VPN. The mirror proxies the public npm registry and also hosts your @acme packages:
.npmrc
checkly.config.ts
embeddownloads the@acmepackages through the mirror on your machine, where the VPN is available, and ships them in the code bundle.runner.registriessends every other package to the public npm registry instead of the mirror. Embedded packages take priority over routing rules, so the@acmepackages never reach the**rule.
checkly test and checkly deploy with the VPN connected, so the CLI can download the embedded packages.
Package name patterns
bundle.packages.embed, bundle.packages.prune, and runner.registries share the same pattern syntax:
Exclusions only remove packages selected by entries before them, so order matters:
['@acme/*', '!@acme/legacy'] selects the scope except @acme/legacy, while ['!@acme/legacy', '@acme/*'] selects the whole scope.
Troubleshooting
See what was embedded or pruned
Run the CLI with debug logging to see which packages eachembed entry matched, and why lockfile pruning was skipped:
Terminal
The code bundle is too large to upload
The compressed bundle exceeds the limit the Checkly API accepts for your account: 30 MB, unless it has been raised. Enterprise plans can raise it to 100 MB. Embedded packages count toward it; see Code bundle size.Dependencies are reinstalled after a change
Changing the set of embedded packages, the pruned manifests or lockfile, orrunner.registries invalidates the runner’s dependency cache. The next run installs dependencies from scratch. A prune pattern or a ! exclusion that matches nothing changes none of these, so it leaves the cache untouched.