Workbrew Lock (beta)
Luke Hefson
The missing piece for guaranteed, version-locked Homebrew installs across teams, projects, and platforms. If you’ve ever experienced a broken build because someone ran brew upgrade or tried to replicate an environment across macOS, Linux and WSL, you know the pain. Homebrew's principle of rolling releases makes it almost impossible to stay on one version. Workbrew Lock fixes that so you can focus on coding, not configuration.
TL;DR example

To have NodeJS versioned through Workbrew Lock and Git run:
# First, install and setup Workbrew on the Device
# Second, create HOMEBREW_WORKBREW_LOCKFILE=1 Brew Configuration in the Workbrew Console
$ brew bundle add node
$ brew bundle install
$ git add Brewfile Brewfile.workbrew.lock.json
$ git commit -m "Add Brewfile and Workbrew Lock"
$ brew bundle exec -- which node
Anyone running brew bundle install with Workbrew will get the same brew bundle exec -- which node output, reflecting the NodeJS version in the Git-tracked lock file.
Features
Workbrew Lock provides:
-
Versioned formulae lock files (
Brewfile.workbrew.lock.json) to guarantee everyone on your team/project is running the same versions. -
A
brew bundlewrapper that uses these versions intelligently to manage your environment, shell and services with these versioned tools. -
Acts as a replacement for or integrates with language version managers such as
rbenv,nodenvandpyenv. -
Parallelised/faster downloads and checks for
brew bundleandBrewfilesthrough our Go-based Workbrew Agent. -
Defaults for
brew bundlebased on the above i.e. no versions are upgraded without explicit request, locked versions are not cleaned up.
New Issues/Feedback
Please contact us through the details on the Contact page.
In this beta, the older your locked versions are, the more likely you are to encounter issues.
If you're blocked, brew bundle upgrade fixes all known issues.
Basic Usage
If you've never heard of brew bundle or Brewfiles before: check out the Homebrew brew bundle and Brewfile documentation.
Assuming you've read (or skimmed) most of that, here's the next steps:
-
Install Workbrew on your device (if you haven't already)
-
Ensure
which brewin your shell/terminal outputs/opt/workbrew/bin/brew -
Set
export HOMEBREW_WORKBREW_LOCKFILE=1in your shell environment (e.g. add it to your.zshrcor.bash_profile) or create a Brew Configuration in the Workbrew Console with the keyHOMEBREW_WORKBREW_LOCKFILEand value1to enable use on all Devices in your Workspace -
Enter a directory containing a
Brewfileor create aBrewfilein the current directory -
Run
brew bundle. If==> Using Brewfile.workbrew.lock.jsonis outputted: things are working as expected. -
Note the
Brewfile.workbrew.lock.jsonfile that was created. We recommend committing it to version control for use by your team.
You can then use brew bundle check and brew bundle install in your scripts and CI workflows.
Advanced Usage
Workbrew Lock really shines when used with the commands brew bundle exec, brew bundle sh and brew bundle env.
brew bundle exec
As in Homebrew Bundle, brew bundle exec will run a command in an environment customised by your Brewfile.
Unlike Homebrew Bundle, Workbrew Lock will also use versions.
For example, with a Brewfile like:
brew "node", version_file: ".node-version"
And a Brewfile.workbrew.lock.json like:
{
"formulae": [
{
"name": "node",
"version": "23.11.0"
}
]
}
This will ensure you are always running the specific version of node:
$ brew bundle exec which node
==> Using Brewfile.workbrew.lock.json
/opt/homebrew/Cellar/node/23.11.0/bin/node
Regardless of how your team's environments are configured, regardless of running brew upgrade node in Homebrew: this version will be fixed until you run brew bundle upgrade or brew bundle --upgrade-formulae node
Using brew bundle exec --services will also start any services in your Brewfile at the locked version. This is particularly useful for e.g. MySQL, PostgreSQL, etc. where it's useful to match your local development database version with the production version.
brew bundle sh
As in Homebrew Bundle, brew bundle sh will create a new interactive shell (or "virtual environment") with the environment setup.
Unlike Homebrew Bundle, Workbrew Lock will configure the environment with all tools at the same specific versions.
Using brew bundle sh --services will also start your shell with any services in your Brewfile at the locked version.
brew bundle env
As in Homebrew Bundle, brew bundle env dumps out all the environment variables in a form suitable for adding to a shell.
Unlike Homebrew bundle, Workbrew Lock will include environment variables for specific versions of tools and cache this data for speed. A .Brewfile.workbrew.lock.cache.json is written which allows future invocations of brew bundle env to take 0.01s. You should add .Brewfile.workbrew.lock.cache.json to gitignore as it may include device-specific output.
This enables brew bundle env with Workbrew Lock to be used with direnv in .envrc files:
layout_workbrew_lock() {
export HOMEBREW_WORKBREW_LOCKFILE=1
brew bundle check >/dev/null && eval "$(brew bundle env)"
watch_file .Brewfile.workbrew.lock.cache.json
}
layout workbrew_lock
This means changing to a new directory will automatically setup the Homebrew environment.
Disabling Workbrew Lock
By default, Workbrew Lock will attempt to write a Brewfile.workbrew.lock.json for every Brewfile with formulae.
If you wish to disable this you can:
- add
# workbrew-lock:disableto theBrewfile
or:
- create a
.Brewfile.workbrew.lock.disabledfile in the same directory as theBrewfile