Run services with a specific Node version (nvm / fnm)
You point Runyard at your project, it starts the service, and you get command not found: node or worse, the system Node (the wrong version) runs and your build fails. The cause is the same in both cases.
The trick
nvm and fnm are shell functions, not standalone binaries. They install themselves by adding init code to your .zshrc or .bashrc. Runyard's spawned environment is sanitized and does not load those files, so neither tool initializes and the node they expose is invisible.
There are two clean fixes, each with tradeoffs.
Option A: Pin a Node version in paths
Add the exact node binary directory to Runyard's paths array. Works for any service. No shell wrapping.
{
"paths": [
"~/.nvm/versions/node/v22.0.0/bin",
"/opt/homebrew/bin"
],
"tools": [
{
"name": "API",
"type": "service",
"directory": "~/Code/your-project",
"startCommands": [{
"label": "Dev",
"command": "npm",
"args": ["run", "dev"],
"startupCheck": "http://localhost/health",
"startupFallbackPort": 3000
}]
}
]
}
Pros: Simple. No shell startup overhead. Works the same for all services that need this Node.
Cons: The path is hard-coded. When you upgrade Node, you have to edit config.json to bump the version. Different projects on different Node versions need different paths arrays per tool (use paths at the tool level with pathsOverride: false to merge).
Option B: Wrap the command in a login shell (recommended)
Let your shell init files do their job. Runyard runs the command, your shell sources .zshrc, nvm/fnm initialize, and .nvmrc / .node-version is respected automatically.
{
"name": "API",
"type": "service",
"directory": "~/Code/your-project",
"startCommands": [{
"label": "Dev",
"command": "/bin/zsh",
"args": ["-lc", "npm run dev"],
"startupCheck": "http://localhost/health",
"startupFallbackPort": 3000
}]
}
Pros: Auto-switches Node version based on .nvmrc / .node-version. No config drift when you upgrade Node. Works identically to running npm run dev in your terminal.
Cons: Slightly slower startup (a few hundred milliseconds for shell init). All your .zshrc runs every time, including unrelated prompts and aliases.
Why this works
/bin/zsh -lc 'cmd'starts zsh as a login shell (-l), which loads.zprofile,.zshrc, and other init files.-c 'cmd'then runs your command in that shell.- nvm and fnm's init scripts register hooks that read
.nvmrc/.node-versionfrom the current directory. Since Runyard sets the working directory to your tool'sdirectory, the right version is picked automatically. - After your command exits, the shell exits. No interactive prompt.
Prerequisites
- nvm or fnm installed and properly configured in your shell init files (you can verify by running
node --versionin a new terminal window from inside your project) - An
.nvmrcor.node-versionfile in your project root (optional but recommended for Option B)