Skip to content

Container Execution

LazyWorktree can run custom commands inside OCI containers (Docker or Podman), giving each worktree an isolated, reproducible environment without requiring local toolchains.

Use this page when: you want to run custom commands inside Docker or Podman containers, with automatic worktree mounting.

Overview

Container execution enables you to:

  • Run builds and tests in isolated containers with pinned toolchain versions
  • Reproduce CI environments locally without installing dependencies
  • Combine containers with multiplexer sessions for multi-window container workflows
  • Auto-detect Docker or Podman — no runtime configuration needed

Basic Examples

Image Only

Specify just an image to launch a container with the image's default entrypoint. The worktree is automatically mounted at /workspace.

custom_commands:
  ctrl+d:
    description: Default shell in container
    container:
      image: "ubuntu:24.04"
      interactive: true

Image + Command

Run a specific command inside the container:

custom_commands:
  T:
    command: "go test ./..."
    description: Tests in container
    show_output: true
    container:
      image: "golang:1.22"

Image + Entrypoint

Override the image's default entrypoint — useful for images that bundle multiple tools or need a specific shell:

custom_commands:
  ctrl+s:
    description: Shell in Python container
    container:
      image: "python:3.12"
      entrypoint: "/bin/sh"
      interactive: true

Tip

entrypoint vs command: The entrypoint overrides the binary that runs inside the container, whilst command provides arguments passed via sh -c. When both are set, the entrypoint runs with the command as its argument. When neither is set, the container runs with its image defaults.

Claude Code in a Container

Run Claude Code inside a container for each worktree. The examples below use --dangerously-skip-permissions and --user to avoid permission issues with mounted host directories.

With Anthropic API

Use the Anthropic API directly. Pass your API key via an environment variable:

custom_commands:
  C:
    description: Claude Code
    command: "claude"
    container:
      interactive: true
      image: ghcr.io/chmouel/agents-image
      args:
        - "--model=claude-sonnet-4-6@default"
        - "--dangerously-skip-permissions"
      env:
        CLAUDE_CONFIG_DIR: "/claude"
        ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY}"
      mounts:
        - source: "~/.claude"
          target: "/claude"
      extra_args:
        - "--user=1000:1000"

Tip

The --user=1000:1000 flag runs the container as your host UID, preventing EACCES errors when writing to mounted directories like ~/.claude/debug/. Replace 1000:1000 with your actual UID/GID if different (check with id -u and id -g).

With Vertex AI

Use Google Cloud Vertex AI as the backend. Mount your gcloud credentials and set the required environment variables:

custom_commands:
  C:
    description: Claude Code (Vertex)
    command: "claude"
    container:
      interactive: true
      image: ghcr.io/chmouel/agents-image
      args:
        - "--model=claude-sonnet-4-6@default"
        - "--dangerously-skip-permissions"
      env:
        CLAUDE_CONFIG_DIR: "/claude"
        CLAUDE_CODE_USE_VERTEX: 1
        CLOUD_ML_REGION: us-east5
        ANTHROPIC_VERTEX_PROJECT_ID: my-gcp-project-id
      mounts:
        - source: "~/.claude"
          target: "/claude"
        - source: "~/.config/gcloud"
          target: "/home/ubuntu/.config/gcloud"
      extra_args:
        - "--user=1000:1000"

Combining with Multiplexers

Container execution can be combined with tmux or zellij. Each window/tab command is individually wrapped in a container invocation, so every pane runs inside the same image.

Container + tmux

custom_commands:
  ctrl+l:
    description: Dev container
    tmux:
      session_name: "dev:$WORKTREE_NAME"
      windows:
        - name: test
          command: "go test -v ./..."
        - name: shell
    container:
      image: "golang:1.22"
      extra_args:
        - "--network=host"

Container + zellij

custom_commands:
  ctrl+z:
    description: Node dev container
    zellij:
      session_name: "dev:$WORKTREE_NAME"
      windows:
        - name: server
          command: "npm run dev"
        - name: test
          command: "npm test"
        - name: shell
    container:
      image: "node:20"

Mounts and Environment

Automatic Mount

The worktree path is automatically bind-mounted to the container's working directory (/workspace by default). No configuration needed.

If you specify a custom mount targeting the same path as working_dir, the automatic mount is skipped.

Custom Mounts

Add extra bind mounts for caches, credentials, or shared data:

custom_commands:
  B:
    command: "go build ./..."
    description: Build with cache
    container:
      image: "golang:1.22"
      mounts:
        - source: ~/.cache/go-build
          target: /root/.cache/go-build
        - source: ~/go/pkg/mod
          target: /root/go/pkg/mod
          read_only: true
        - source: "~/.local/share/worktrees/.claude"
          target: "/claude"
          options: "z"   # SELinux: relabel for shared container access

Each mount supports:

Field Type Description
source string Host path (supports ~ and env var expansion)
target string Container path
read_only bool Mount as read-only (default: false)
options string Comma-separated Docker/Podman volume options (e.g. z for SELinux shared relabeling, Z for private relabeling)

Environment Variables

Forward environment variables into the container:

container:
  image: "node:20"
  env:
    NODE_ENV: development
    API_KEY: "${API_KEY}"

WORKTREE_* environment variables (WORKTREE_BRANCH, WORKTREE_PATH, WORKTREE_NAME, MAIN_WORKTREE_PATH, REPO_NAME) are forwarded into the container automatically.

Advanced

Extra Arguments

Pass additional flags to the docker run or podman run invocation:

container:
  image: "golang:1.22"
  extra_args:
    - "--network=host"
    - "--cpus=2"
    - "--memory=4g"

Interactive Mode

Allocate a TTY for interactive use (adds -it flags):

container:
  image: "ubuntu:24.04"
  interactive: true

Runtime Selection

By default, LazyWorktree auto-detects the container runtime, preferring Podman over Docker. Override with:

container:
  image: "golang:1.22"
  runtime: docker   # or "podman"

Working Directory

Change the mount point inside the container:

container:
  image: "golang:1.22"
  working_dir: /src

Field Reference

For the complete list of container fields and mount options, see the Container Fields section in the Custom Commands reference.