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
# Install vlt globally
$ npm install -g vlt
# Configure a registry -- vlt has no default
$ vlt config set registry=https://registry.npmjs.org/
# In your existing project, run:
$ vlt install $ vlt buildUnlike yarn, vlt has no default registry — commands that resolve
or fetch packages fail with a config error until one is set. The
snippet above points it at the public npm registry; if you have a
vlt.io account, run
vlt setup
instead to configure your account's registry aliases. See
Registries.
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
| yarn | vlt | Notes |
|---|---|---|
yarn / yarn install | vlt install | Does 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 init | vlt init | |
yarn pack | vlt pack | |
yarn npm publish | vlt publish | |
yarn npm login | vlt login | |
yarn npm whoami | vlt whoami | |
yarn workspaces list | vlt list | |
yarn why <pkg> | vlt query '#<pkg>' | DSS query; see Selectors |
yarn install --frozen-lockfile | vlt install --frozen-lockfile | |
yarn install --immutable | vlt 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:
$ vlt config set fallback-command=run-execAfter 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 (yarn berry)
npmRegistryServer: "https://registry.internal.company.com/"
npmScopes:
mycompany:
npmRegistryServer: "https://npm.mycompany.com/"
npmAuthToken: "abc123"vlt equivalent:
{
"registry": "https://registry.internal.company.com/",
"scoped-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 (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:
{
"scoped-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:
{
"registries": {
"internal": "https://npm.mycompany.com/"
}
}{
"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.
# Log in to default registry
$ vlt login
# Log in to a custom registry
$ vlt login --registry=https://npm.mycompany.com/CI Environments
# yarn v2+
YARN_NPM_AUTH_TOKEN=abc123 yarn install
# vlt
VLT_TOKEN=abc123 vlt installSee 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:
- Run
vlt installto generatevlt-lock.json - Commit
vlt-lock.json - Optionally remove
yarn.lockonce 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:
# Phase 1: Install (no code executes)
$ vlt install
# Phase 2: Build (runs scripts, skipping known malware)
$ vlt buildBy 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:
# 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:
{
"private": true,
"workspaces": [
"packages/*"
]
}yarn v2+
yarn v2+ is similar but also supports the workspaces field in
.yarnrc.yml for additional filtering.
vlt
Your existing yarn workspaces just work. vlt reads the
workspaces field of the root package.json, in both the array form
above and the object form, where the nohoist key is parsed and
ignored:
{
"private": true,
"workspaces": {
"packages": [
"packages/*"
],
"nohoist": [
"**/react-native"
]
}
}Exclusion patterns such as !packages/legacy are honored too. The
workspaces field of .yarnrc.yml is not read, so any yarn v2+
filtering defined there needs to move.
You only need to define workspaces in vlt.json if you want vlt's
named workspace groups for targeted operations. If vlt.json has
a workspaces field it wins outright, and the package.json field is
ignored rather than merged:
{
"workspaces": {
"apps": "apps/*",
"libs": [
"packages/*",
"shared/*"
]
}
}# Run tests only in the libs group
$ vlt run test -g libs
# Run build across all workspaces
$ vlt run build --recursiveWorkspace Commands
| yarn | vlt |
|---|---|
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.
{
"resolutions": {
"lodash": "^4.17.21",
"express/qs": "6.10.0"
}
}{
"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 supportsfile:,workspace:,git:, andregistry:specifiers
Migration Checklist
- Install vlt:
npm install -g vlt - Create
vlt.jsonwith your registry configuration - Move scoped registry config from
.yarnrc.yml/.npmrctovlt.json - Leave your
workspacesfield inpackage.json— vlt reads it as-is. Move it tovlt.jsononly if you want named groups - Move
resolutionsfrompackage.jsontomodifiersinvlt.json - If using PnP, remove
.pnp.cjs,.pnp.loader.mjs, and related.yarnrc.ymlsettings - Run
vlt installthenvlt build - Commit
vlt-lock.json - Update CI scripts: replace
yarn install --immutablewithvlt install --expect-lockfile && vlt build - Update CI auth: replace
YARN_NPM_AUTH_TOKENwithVLT_TOKEN - Update any
yarn dlxusage tovlx