Monorepos
Copy pageTips for using workmux with monorepos containing multiple services.
Nested configuration
Section titled “Nested configuration”Place a .workmux.yaml in any subdirectory to configure that project independently. When you run workmux from a subdirectory, it walks upward to find the nearest config:
monorepo/├── .workmux.yaml # Root config (used from monorepo/)├── backend/│ ├── .workmux.yaml # Backend config (used from backend/)│ └── src/└── frontend/ ├── .workmux.yaml # Frontend config (used from frontend/) └── src/cd monorepo/backendworkmux add api-feature # Uses backend/.workmux.yamlWhen using a nested config:
- Working directory: The tmux window opens in the subdirectory (e.g.,
backend/) within the new worktree, not the worktree root - File operations:
files.copyandfiles.symlinkpaths are relative to the config directory - Hooks: Run with the subdirectory as the working directory
Example nested config
Section titled “Example nested config”agent: claude
files: copy: - .env # Copies backend/.env to worktree's backend/.env
post_create: - cargo build # Runs in worktree's backend/ directoryEnvironment variables
Section titled “Environment variables”Hooks receive WM_CONFIG_DIR pointing to the config directory in the new worktree:
post_create: - echo "Project root: $WM_PROJECT_ROOT" # /path/to/main-worktree - echo "Config dir: $WM_CONFIG_DIR" # /path/to/worktree/backend - echo "Worktree root: $WM_WORKTREE_PATH" # /path/to/worktreeNo inheritance
Section titled “No inheritance”Nested configs completely replace the root config - they are not merged. If you need shared settings across projects, use the global config (~/.config/workmux/config.yaml) with the <global> placeholder.
Port isolation
Section titled “Port isolation”When running multiple services (API, web app, database) in a monorepo, each worktree needs unique ports to avoid conflicts. For example, if your .env has hardcoded ports like API_PORT=3001 and VITE_PORT=3000, running two worktrees simultaneously would fail because both would try to bind to the same ports.
One strategy is to generate a .env.local file with unique ports for each worktree. Many frameworks (Vite, Next.js, CRA) automatically load .env.local and merge it with .env, with .env.local taking precedence.
Example
Section titled “Example”Create a script at scripts/worktree-env:
#!/usr/bin/env bashset -euo pipefail
port_in_use() { lsof -nP -iTCP:"$1" -sTCP:LISTEN &>/dev/null}
find_port() { local port=$1 while port_in_use "$port"; do ((port++)) done echo "$port"}
# Hash the handle to get a deterministic port offset (0-99)hash=$(echo -n "$WM_HANDLE" | md5 | cut -c1-4)offset=$((16#$hash % 100))
# Find available ports starting from the hash-based offsetapi_port=$(find_port $((3001 + offset * 10)))vite_port=$(find_port $((3000 + offset * 10)))
# Generate .env.local with port overridescat >.env.local <<EOFAPI_PORT=$api_portVITE_PORT=$vite_portVITE_PUBLIC_API_URL=http://localhost:$api_portEOF
echo "Created .env.local with ports: API=$api_port, VITE=$vite_port"Configure workmux to copy .env and generate .env.local:
files: copy: - .env # Copy secrets (DATABASE_URL, API keys, etc.)
post_create: - ./scripts/worktree-env # Generate .env.local with unique portsPlain Node.js
Section titled “Plain Node.js”For Node.js without framework support, load both files with later overriding earlier:
{ "scripts": { "api": "node --env-file=.env --env-file=.env.local api/server.js", "web": "node --env-file=.env --env-file=.env.local web/server.js" }}Using direnv
Section titled “Using direnv”You can also use direnv to load the generated .env.local:
dotenvdotenv_if_exists .env.localUse the same worktree-env script to generate .env.local. When you enter the directory, direnv automatically loads .env and .env.local, with the latter taking precedence.
files: copy: - .envrc - .env
post_create: - ./scripts/worktree-envHow it works
Section titled “How it works”The worktree handle is hashed to get a deterministic starting port, so feature-auth always starts at the same offset. If that port is taken, lsof finds the next available one.
$ workmux add feature-authRunning setup commands...Created .env.local with ports: API=3471, VITE=3470✓ Setup complete✓ Successfully created worktree and tmux window for 'feature-auth'