Load .envrc or mise.toml before starting a service
Your project relies on direnv, mise, or asdf to manage env vars, secrets, and tool versions. In a terminal it all works because your shell init hooks them in. Runyard starts your service with a clean environment, none of those hooks fire, and the service runs with stale or missing config.
The trick
direnv and mise both ship an exec subcommand that applies the project-local env to a child process without any shell hooks. You wrap your real command with it. asdf does not have a clean exec equivalent, so for asdf the cleanest path is wrapping the command in a login shell.
Working configuration
direnv
{
"name": "API",
"type": "service",
"directory": "~/Code/your-project",
"startCommands": [{
"label": "Dev",
"command": "direnv",
"args": ["exec", ".", "npm", "run", "dev"]
}],
"paths": ["/opt/homebrew/bin"]
}
direnv exec <path> <cmd...> loads <path>/.envrc, applies its env, then executes cmd in that environment. The . means "use the directory we are in" (which is the tool's directory).
mise
{
"startCommands": [{
"label": "Dev",
"command": "mise",
"args": ["exec", "--", "npm", "run", "dev"]
}]
}
mise exec -- <cmd...> applies project-local tool versions and env from mise.toml or .mise.toml, then runs cmd. The -- separator is required: without it, mise tries to interpret npm run dev as its own flags.
asdf (no native exec)
asdf does not expose an exec subcommand, so fall back to the login-shell wrapper pattern. Make sure asdf's shims directory is in paths or sourced by your login shell.
{
"startCommands": [{
"label": "Dev",
"command": "/bin/zsh",
"args": ["-lc", "npm run dev"]
}]
}
Why this works
direnv execandmise execare pure child-process commands. They read project files, build an env, andexecveinto your command. No shell interaction, no init files.- The working directory is set by Runyard from your tool's
directory, so direnv/mise look in the right place. - For asdf, the login shell sources
~/.asdf/asdf.shfrom your.zshrc, which sets up the shims so the right tool versions are picked.
Prerequisites
- direnv, mise, or asdf installed (via Homebrew typically:
brew install direnv mise asdf) - The tool's binary directory in Runyard's
pathsconfig so the command resolves./opt/homebrew/bincovers the Homebrew install path on Apple Silicon. - For direnv only: run
direnv allowonce in a terminal inside the project. direnv refuses to source untrusted.envrcfiles. This is a security feature, not a Runyard quirk. Without it, the service starts but env vars are missing. - For mise only: make sure the project's
mise.tomlis committed and any required tools are already installed (mise install).
Gotcha: the direnv allow prompt is invisible from Runyard
direnv normally prompts in the terminal when it detects a new or modified .envrc. Inside Runyard there is no terminal to prompt, so direnv silently emits a "blocked" log and falls back to the parent env. If your service starts but acts like env vars are missing, open a terminal in the project directory and check direnv status.