Skip to content

Migrating from yarn to vlt

This guide covers migration from both yarn v1 (classic) and yarn v2+ (berry). Key differences are called out where they apply.

Quick Start

Terminal
# Install vlt globally
$ npm install -g vlt
# In your existing project, run:
$ vlt install $ vlt build

vlt reads your existing package.json files and resolves dependencies. The yarn.lock file is not migrated — vlt performs a fresh resolution and creates vlt-lock.json.


Command Mapping

yarnvltNotes
yarn / yarn installvlt installDoes not run lifecycle scripts
yarn add <pkg>vlt install <pkg>
yarn add -D <pkg>vlt install -D <pkg>
yarn remove <pkg>vlt uninstall <pkg>
yarn run <script>vlt run <script>
yarn <script>vlt run <script>See fallback-command
yarn dlx <pkg> / npx <pkg>vlx <pkg>Run remote packages
yarn exec <cmd>vlt exec <cmd>
yarn initvlt init
yarn packvlt pack
yarn npm publishvlt publish
yarn npm loginvlt login
yarn npm whoamivlt whoami
yarn workspaces listvlt list
yarn why <pkg>vlt query '#<pkg>'DSS query; see Selectors
yarn install --frozen-lockfilevlt install --frozen-lockfile
yarn install --immutablevlt install --expect-lockfile
yarn config set <key> <val>vlt config set <key>=<val>
yarn up <pkg>vlt install <pkg>Re-resolves to latest matching

Shorthand Script Execution

Yarn lets you run scripts without run (e.g., yarn build). vlt supports this via the fallback-command config:

Terminal
$ vlt config set fallback-command=run-exec

After setting this, vlt build will first check for a vlt command named build, and if none matches, look for a package.json script.


Configuration

.yarnrc.yml / .yarnrc → vlt.json

yarn v1 uses .yarnrc and .npmrc. yarn v2+ uses .yarnrc.yml. vlt uses vlt.json.

yarn v2+ example:

.yarnrc.yml
# .yarnrc.yml (yarn berry)
npmRegistryServer: "https://registry.internal.company.com/"
npmScopes:
mycompany:
npmRegistryServer: "https://npm.mycompany.com/"
npmAuthToken: "abc123"

vlt equivalent:

vlt.json
{
"registry": "https://registry.internal.company.com/",
"scope-registries": {
"@mycompany": "https://npm.mycompany.com/"
}
}

Auth tokens are stored separately in vlt’s keychain, not in config files. See Authentication below.

yarn v1 example:

.npmrc
# .npmrc (yarn classic)
registry=https://registry.internal.company.com/
@mycompany:registry=https://npm.mycompany.com/

Same vlt.json as above.


Registry Configuration

Scoped Registries

Both yarn and vlt support scope-to-registry mapping:

vlt.json
{
"scope-registries": {
"@mycompany": "https://npm.mycompany.com/"
}
}

Named Registry Aliases

vlt also supports named registry aliases, which are more explicit than scoped registries. Dependencies reference a named registry directly, removing ambiguity:

vlt.json
{
"registries": {
"internal": "https://npm.mycompany.com/"
}
}
package.json
{
"dependencies": {
"@mycompany/utils": "internal:@mycompany/utils@^2.0"
}
}

Authentication

yarn v1 stores tokens in .npmrc. yarn v2+ stores them in .yarnrc.yml. Both approaches risk accidental commits of secrets.

vlt stores auth tokens in an XDG-compliant keychain file, separate from project configuration.

Terminal
# Log in to default registry
$ vlt login
# Log in to a custom registry
$ vlt login --registry=https://npm.mycompany.com/

CI Environments

Terminal
# yarn v2+
YARN_NPM_AUTH_TOKEN=abc123 yarn install
# vlt
VLT_TOKEN=abc123 vlt install

See Authentication for full details.


Lockfile

yarn v1 uses yarn.lock (custom format). yarn v2+ also uses yarn.lock (YAML-based). vlt uses vlt-lock.json.

When you first run vlt install, vlt creates vlt-lock.json from a fresh resolution. Your yarn.lock is not read.

What to do:

  1. Run vlt install to generate vlt-lock.json
  2. Commit vlt-lock.json
  3. Optionally remove yarn.lock once you’ve fully switched

Install Script Protection

This is a major difference from yarn.

yarn v1 runs all lifecycle scripts automatically. yarn v2+ runs scripts for direct dependencies but blocks transitive dependency scripts by default (you can allowlist via .yarnrc.yml).

vlt goes further: vlt install runs no scripts at all by default. The build step is completely separate:

Terminal
# Phase 1: Install (no code executes)
$ vlt install
# Phase 2: Build (runs scripts, skipping known malware)
$ vlt build

By default, vlt build uses the target :scripts:not(:built):not(:malware) — it only runs scripts for packages that need building and aren’t flagged as malware by Socket.

You can target scripts precisely:

Terminal
# Only allow specific trusted packages
$ vlt build --target="#esbuild, #node-gyp"
# Persist your choice
$ vlt config set "command.build.target=#esbuild, #node-gyp"

See vlt build for full details.


Workspaces

yarn v1

yarn v1 defines workspaces in the root package.json:

package.json (yarn v1)
{
"private": true,
"workspaces": [
"packages/*"
]
}

yarn v2+

yarn v2+ is similar but also supports the workspaces field in .yarnrc.yml for additional filtering.

vlt

vlt defines workspaces in vlt.json:

vlt.json
{
"workspaces": [
"packages/*"
]
}

vlt also supports named workspace groups for targeted operations:

vlt.json
{
"workspaces": {
"apps": "apps/*",
"libs": [
"packages/*",
"shared/*"
]
}
}
Terminal
# Run tests only in the libs group
$ vlt run test -g libs
# Run build across all workspaces
$ vlt run build --recursive

Workspace Commands

yarnvlt
yarn workspace <name> <cmd>cd <path> && vlt <cmd> or vlt <cmd> -w <path>
yarn workspaces foreach <cmd>vlt <cmd> --recursive

See Workspaces for full details.


Resolutions → Graph Modifiers

yarn uses resolutions in package.json to force dependency versions. vlt uses Graph Modifiers in vlt.json.

package.json (yarn)
{
"resolutions": {
"lodash": "^4.17.21",
"express/qs": "6.10.0"
}
}
vlt.json
{
"modifiers": {
"#lodash": "^4.17.21",
":root > #express > #qs": "=6.10.0"
}
}

Graph Modifiers use DSS selectors, which give you more precise control over which instances of a dependency are affected.


Plug’n’Play (PnP)

If you’re using yarn v2+ with Plug’n’Play (no node_modules), the switch to vlt means going back to a node_modules-based layout. This is generally straightforward — vlt creates a standard node_modules directory.

If your project relies on PnP-specific features (like .pnp.cjs loaders), you’ll need to remove those references from your build tooling and runtime configuration.


Features Not in vlt

Some yarn-specific features don’t have direct equivalents:

  • Plug’n’Play / Zero-Installs — vlt uses node_modules
  • Constraints — Use vlt’s DSS query system for dependency policy enforcement
  • Patches (yarn patch) — Not yet available in vlt
  • Protocols (portal:, patch:) — vlt supports file:, workspace:, git:, and registry: specifiers

Migration Checklist

  1. Install vlt: npm install -g vlt
  2. Create vlt.json with registry and workspace configuration
  3. Move scoped registry config from .yarnrc.yml / .npmrc to vlt.json
  4. Move workspace definitions from package.json to vlt.json
  5. Move resolutions from package.json to modifiers in vlt.json
  6. If using PnP, remove .pnp.cjs, .pnp.loader.mjs, and related .yarnrc.yml settings
  7. Run vlt install then vlt build
  8. Commit vlt-lock.json
  9. Update CI scripts: replace yarn install --immutable with vlt install --expect-lockfile && vlt build
  10. Update CI auth: replace YARN_NPM_AUTH_TOKEN with VLT_TOKEN
  11. Update any yarn dlx usage to vlx