Brunch
Brunch runs isolated development environments for Git branches and worktrees.
Each branch gets its own environment. With Compose, that means a separate project, network, containers, and named volumes. Branches checked out in the same worktree share its host port, while additional worktrees get different ports and can run in parallel.
Docker Compose is the default manager. Podman Compose, local processes, and custom project commands are also supported.
Requirements
- Ruby 3.1+
- Git 2.28+
- Docker Compose, Podman Compose, or another supported manager
- git-hooks-ext
Installation
Install git-hooks-ext so that ghe is available on your PATH, then install Brunch:
gem install brunchInside each repository you want Brunch to manage:
brunch installBrunch installs its hooks through git-hooks-ext and will not overwrite hooks owned by another tool.
Configuration
Add brunch.yml to the repository root.
For Docker Compose:
compose_file: compose.yamlFor a Rails app with SQLite databases in storage/, add Dockerfile.dev:
FROM ruby:3.4-slim
WORKDIR /rails
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libsqlite3-dev libvips libyaml-dev pkg-config && \
rm -rf /var/lib/apt/lists/*
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .Use the Ruby version required by your application. Then add compose.yaml:
services:
web:
build:
context: .
dockerfile: Dockerfile.dev
command: sh -c 'bin/rails db:prepare && exec bin/rails server -b 0.0.0.0 -p 3000'
environment:
RAILS_ENV: development
volumes:
- .:/rails
- storage:/rails/storage
ports:
- "127.0.0.1:${BRUNCH_PORT}:3000"
volumes:
storage:The bind mount makes source edits visible to Rails. The named volume keeps the
SQLite database separate for each branch and preserves it across container
rebuilds. If your app stores its database elsewhere, mount that location instead.
Brunch runs on the host, so it does not need to be in the Rails Gemfile.
After changing Gemfile or Gemfile.lock, run brunch restart to rebuild the
image with the new gems; the named volume remains intact.
The first activated worktree prefers port 3000. Additional worktrees receive another free port.
To prefer a different starting port:
preferred_port: 4000Port assignments are persisted per worktree in .git/brunch/state.json.
Getting started
Commit brunch.yml and the application configuration first. The repository must contain at least one commit.
Then run:
brunch install
brunch doctor
brunch activate
brunch statusbrunch activate starts the environment for the currently checked-out branch.
To print only the current worktree's port:
brunch portOpen the application on the printed port, for example:
http://127.0.0.1:3000
With Compose, the application must listen on the container port mapped in the Compose file.
Branch switching
Once the hooks are installed, normal Git branch switches automatically stop the previous branch environment and start the new one:
git switch -c feature/login
git switch mainBranches checked out in the same worktree reuse that worktree's host port.
With Compose, each branch keeps its own Compose project and named volumes, so persistent resources remain isolated between branches. Returning to a previously activated branch starts its stopped containers again, preserving files written inside them. The branch keeps the same host port.
To manually start the environment after brunch stop:
brunch activateTo rebuild and recreate its containers explicitly:
brunch restartbrunch restart may discard data stored only in a container's writable layer.
Put important data, such as databases, in named volumes.
Compose builds use files from the live worktree, including uncommitted changes.
If you want source edits to appear in an already running container without rebuilding, configure a bind mount or another reload mechanism in your Compose setup.
Parallel worktrees
Additional worktrees run independently on different host ports.
Create them with git-hooks-ext:
ghe worktree add -b feature-a ../feature-a
ghe worktree add -b feature-b ../feature-bThen:
cd ../feature-a
brunch port
brunch portsbrunch port prints the current worktree's port.
brunch ports groups the current and previously activated branches under each
worktree. Stopped branches appear in gray and retain their worktree's port for
the next activation.
Switching branches inside one worktree does not affect environments running in other worktrees.
If you create a worktree with plain Git:
git worktree add -b feature-c ../feature-cactivate Brunch manually inside it:
cd ../feature-c
brunch activateAfter moving or removing worktrees with plain Git, run:
brunch cleanupBrunch never deletes the worktree source directory.
Managers
Docker Compose
Docker Compose is the default manager:
compose_file: compose.yamlPodman Compose
Podman Compose uses the same Compose file contract:
manager: podman_compose
compose_file: compose.yamlLocal process
Use local_process for a development command such as bin/dev:
manager: local_process
command: bin/devBrunch starts and stops the process together with the branch environment and stores its output under .git/brunch/controls.
Custom commands
Use the command manager to integrate Brunch with project-specific tooling:
manager: command
commands:
create: bin/environment create
start: bin/environment start
stop: bin/environment stop
remove: bin/environment removeBrunch runs these commands from the live worktree with:
BRUNCH_REF
BRUNCH_PORT
BRUNCH_PROJECT
BRUNCH_SNAPSHOT
BRUNCH_SNAPSHOT points to the live worktree for compatibility with manager adapters.
create is optional.
start must return after launching the environment.
stop is called when switching away from the active branch.
remove is called when the corresponding worktree environment is deleted and should remove manager-owned persistent resources.
Optional commands:
commands:
status: bin/environment status
health: bin/environment health
logs: bin/environment logsThese power the corresponding Brunch operations.
Worktree lifecycle
A branch checkout stops the previous environment in that worktree and starts the new one on the same host port.
Other worktrees continue running.
Removing a worktree removes its Brunch environments. Brunch keeps the information needed to shut down manager resources under .git/brunch/controls, so cleanup can still run after the worktree itself is gone.
If a custom remove command depends on project files, those files must be committed because cleanup after worktree removal uses the last committed state.
brunch cleanup removes environments belonging to worktrees that no longer exist.
Git does not expose every branch deletion workflow reliably to hooks, so stopped branch environments may remain until their worktree is removed.
Commands
brunch installInstall Brunch hooks for the repository.
brunch activateStart the environment for the current branch.
brunch statusShow the current environment, manager status, and port.
brunch portPrint the current worktree's port.
brunch portsList current and stopped branch environments grouped by worktree.
brunch stopStop the active environment without removing it.
brunch restartRecreate and start the active environment.
brunch logs
brunch logs --followShow environment logs.
brunch run -- bin/rails consoleRun a command on the host from the live worktree.
brunch doctorCheck Git, installed hooks, configuration, manager availability, and ports.
brunch cleanupRemove environments belonging to deleted worktrees.
Upgrading from older configurations
Before upgrading from an older branch-only configuration, stop its running environment and clean up its legacy state.
Brunch will not reinterpret an existing non-empty branch-only state as worktree state.
Development
bundle install
bin/install-pre-commit
bundle exec rake quality
gem build brunch.gemspecRun container integration tests with Docker Compose:
BRUNCH_INTEGRATION_MANAGER=docker_compose bundle exec rake testOr Podman Compose:
BRUNCH_INTEGRATION_MANAGER=podman_compose bundle exec rake testWithout BRUNCH_INTEGRATION_MANAGER, container integration tests are skipped by the local quality check.
The pre-commit hook runs RuboCop with automatic corrections, Reek, and the full test suite. SimpleCov requires 100% line and branch coverage for lib/**/*.rb.
If RuboCop modifies a file, review and stage the changes before committing.
