This document explains how to run Hive Mind in Docker containers.
# Pull the latest image
docker pull konard/hive-mind:latest
# Create persistent host directories used by the current Docker workflow
mkdir -p /root/.hive-mind/claude /root/.hive-mind/codex /root/.hive-mind/gh
touch -a /root/.hive-mind/claude.json
# Run the container in detached mode with the same mounts we use locally
docker run -dit --user box --name hive-mind --restart unless-stopped \
-v /root/.hive-mind/claude:/home/box/.claude \
-v /root/.hive-mind/codex:/home/box/.codex \
-v /root/.hive-mind/claude.json:/home/box/.claude.json \
-v /root/.hive-mind/gh:/home/box/.config/gh \
konard/hive-mind:latest bash -l -c 'bash /home/box/start-bot.sh'
# Open a shell in the running container
docker exec -it hive-mind bash
# Inside the container, authenticate with GitHub
gh auth login -h github.com -s repo,workflow,user,read:org,gist
# Authenticate with Claude
claude
# Install or update Codex CLI
bun install -g @openai/codex@latest
# Log in to Codex using the current device auth flow
codex login --device-auth
# Verify Codex after login succeeds with "Successfully logged in"
codex exec --model gpt-5.4-mini "hi"
# Verify Playwright MCP registration in both CLIs
claude mcp list | grep playwright
codex mcp list | grep playwright
# Exit the shell when setup is complete
exit# Build the production image
docker build -t hive-mind:local .
# Run the image
docker run -it hive-mind:localUse konard/hive-mind-dind:latest when the agent must run Docker commands,
Docker Compose, or Testcontainers inside the Hive Mind container.
# Pull the Docker-in-Docker image
docker pull konard/hive-mind-dind:latest
# Default runtime: privileged container starts an inner dockerd
docker run --rm --privileged -it konard/hive-mind-dind:latest bash
# Inside the container, verify nested Docker
docker info
docker run hello-worldThe image defaults the inner Docker daemon to DIND_STORAGE_DRIVER=vfs for
compatibility with overlay-backed hosts. For faster local runs on hosts that
support nested overlay mounts, pass -e DIND_STORAGE_DRIVER=overlay2.
On shared hosts, prefer a Sysbox runtime when it is available:
docker run --rm --runtime=sysbox-runc -it konard/hive-mind-dind:latest bashThe DinD image is published separately from konard/hive-mind:latest so users
who do not need nested Docker keep the existing lower-privilege image.
For development purposes, the legacy Dockerfile provides a Gitpod-compatible environment:
# Build the development image
docker build -t hive-mind-dev .
# Run with credential mounts
docker run --rm -it \
-v ~/.config/gh:/home/box/.persisted-configs/gh:ro \
-v ~/.local/share/claude-profiles:/home/box/.persisted-configs/claude:ro \
-v ~/.config/claude-code:/home/box/.persisted-configs/claude-code:ro \
-v "$(pwd)/output:/home/box/output" \
hive-mind-devThe production Docker image (Dockerfile) extends the pinned full konard/box image, which provides Ubuntu 24.04 plus the general development toolchain. IMPORTANT: Authentication is performed inside the container AFTER the Docker image is fully installed and running.
Why Authentication Happens After Installation:
- ✅ Avoids Docker build timeouts caused by interactive prompts
- ✅ Prevents build failures in CI/CD pipelines
- ✅ Allows the installation script to complete successfully
- ✅ Supports automated Docker image builds
# Inside the container, AFTER it's running
gh auth login -h github.com -s repo,workflow,user,read:org,gistNote: The installation script intentionally does NOT call gh auth login during the build process. This is by design to support Docker builds without timeouts.
# Inside the container, AFTER it's running
claudeInstall or update Codex CLI inside the running container:
bun install -g @openai/codex@latestLog in with the device auth flow we currently use:
codex login --device-authThe command should finish with:
Successfully logged in
Then run the current smoke test:
codex exec --model gpt-5.4-mini "hi"This approach allows:
- ✅ Multiple Docker instances with different GitHub accounts
- ✅ Multiple Docker instances with different Claude subscriptions
- ✅ Persistent Codex authentication and session data when
/home/box/.codexis mounted - ✅ No credential leakage between containers
- ✅ Each container has its own isolated authentication
- ✅ Successful Docker builds without interactive authentication
The image build now registers Playwright MCP for both Claude and Codex:
claude mcp add playwright -s user -- ...codex mcp add playwright -- ...
The CI workflow also builds the Docker image and verifies that:
playwright --versionworks as a CLI fallback;npx --no-install @playwright/mcp --helpworks without reinstalling the MCP package;claude mcp listreports the Playwright server as connected/enabled, not pending or unavailable;codex mcp listreports the Playwright server as connected/enabled, not pending or unavailable.
If you still reproduce codex mcp list showing No MCP servers configured yet in a running container, the most likely root cause is a mounted /home/box/.codex directory from the host. In this image HOME=/home/box, so mounting /home/box/.codex replaces the image-baked Codex config, including any preconfigured MCP entries.
That means:
- the published image can be correct,
- the runtime container can still show Codex as unconfigured,
- and the difference is caused by persisted host state overriding the container defaults.
To confirm that quickly, compare these two cases:
# Fresh container without host-mounted Codex state
docker run --rm -it konard/hive-mind:latest bash -lc 'codex mcp list'
# Container with persisted Codex state from host
docker run --rm -it \
-v /root/.hive-mind/codex:/home/box/.codex \
konard/hive-mind:latest \
bash -lc 'codex mcp list'If the first command shows playwright and the second does not, the host-mounted Codex directory is the source of the mismatch.
- Docker: Install Docker Desktop or Docker Engine (version 20.10 or higher)
- Internet Connection: Required for pulling images and authentication
.
├── Dockerfile # Production image based on konard/box
├── experiments/
│ └── solve-dockerize/
│ └── Dockerfile # Legacy Gitpod-compatible image (archived)
├── scripts/
│ └── verify-docker-image.sh # Docker image verification script
└── docs/
└── DOCKER.md # This file
To persist authentication and work between container restarts, mount the actual per-tool directories instead of a generic /home/box volume. In our Docker images HOME=/home/box, so Codex stores its data in /home/box/.codex.
# Host directories used by the current local Docker workflow
mkdir -p /root/.hive-mind/claude /root/.hive-mind/codex /root/.hive-mind/gh
touch -a /root/.hive-mind/claude.json
# Run with persistent mounts
docker run -dit --user box --name hive-mind --restart unless-stopped \
-v /root/.hive-mind/claude:/home/box/.claude \
-v /root/.hive-mind/codex:/home/box/.codex \
-v /root/.hive-mind/claude.json:/home/box/.claude.json \
-v /root/.hive-mind/gh:/home/box/.config/gh \
konard/hive-mind:latest bash -l -c 'bash /home/box/start-bot.sh'
# Fix ownership after the container starts
BOX_UID=$(docker exec hive-mind id -u box)
chown -R $BOX_UID:$BOX_UID /root/.hive-mind/claude /root/.hive-mind/codex /root/.hive-mind/gh
chown $BOX_UID:$BOX_UID /root/.hive-mind/claude.jsonThe mounted Codex directory keeps the files we rely on:
/home/box/.codex/auth.json/home/box/.codex/config.toml/home/box/.codex/sessions/
Because this mount fully overrides the image's /home/box/.codex directory, it can also preserve an older config.toml that does not include the Playwright MCP registration added by newer images. After starting a container with an older persisted Codex directory, re-run:
codex mcp add playwright -- npx -y @playwright/mcp@latest --isolated --headless --no-sandbox --timeout-action=600000 --viewport-size 1920x1080Hive Mind also attempts this default registration repair at runtime when
codex mcp list has no Playwright row and @playwright/mcp is installed. It
does not overwrite an existing Playwright row that is pending, disabled, or
customized; those states need direct MCP startup debugging.
# Start a detached container with persistent auth mounts
docker run -dit --user box --name hive-worker --restart unless-stopped \
-v /root/.hive-mind/claude:/home/box/.claude \
-v /root/.hive-mind/codex:/home/box/.codex \
-v /root/.hive-mind/claude.json:/home/box/.claude.json \
-v /root/.hive-mind/gh:/home/box/.config/gh \
konard/hive-mind:latest bash -l -c 'bash /home/box/start-bot.sh'
# Execute commands in the running container
docker exec -it hive-worker bash
# Inside the container, run your commands
codex exec --model gpt-5.4-mini "hi"
solve https://github.com/owner/repo/issues/123Create a docker-compose.yml:
version: '3.8'
services:
hive-mind:
image: konard/hive-mind:latest
volumes:
- box-home:/home/box
stdin_open: true
tty: true
volumes:
box-home:Then run:
docker-compose run --rm hive-mind# Inside the container, check authentication status
gh auth status
# Re-authenticate if needed
gh auth login -h github.com -s repo,workflow,user,read:org,gist# Inside the container, re-run Claude to authenticate
claude# Check Docker status on host
docker info
# Pull the latest image
docker pull konard/hive-mind:latest
# Rebuild from source
docker build -t hive-mind:local .If you encounter issues building the image locally:
- Ensure you have enough disk space (at least 20GB free)
- Check your internet connection
- Try building with more verbose output:
docker build -t hive-mind:local --progress=plain .
If you're maintaining a fork or want to publish to your own Docker Hub account, follow these steps to configure GitHub Actions:
- Go to hub.docker.com
- Sign up or log in to your account
- Note your Docker Hub username (e.g.,
konard)
- Log in to hub.docker.com
- Click on your username in the top-right corner
- Select Account Settings → Security
- Click New Access Token
- Enter a description (e.g., "GitHub Actions - Hive Mind")
- Set permissions to Read, Write, Delete (required for publishing)
- Click Generate
- IMPORTANT: Copy the token immediately - you won't be able to see it again!
- Example format:
dckr_pat_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p
- Example format:
-
Go to your GitHub repository (e.g.,
https://github.com/konard/hive-mind) -
Click Settings → Secrets and variables → Actions
-
Click New repository secret
-
Add the following two secrets:
Secret 1: DOCKERHUB_USERNAME
- Name:
DOCKERHUB_USERNAME - Value: Your Docker Hub username (e.g.,
konard) - Click Add secret
Secret 2: DOCKERHUB_TOKEN
- Name:
DOCKERHUB_TOKEN - Value: The access token you generated in Step 2
- Click Add secret
- Name:
If using a fork, update the image name in .github/workflows/docker-publish.yml:
env:
REGISTRY: docker.io
IMAGE_NAME: YOUR_DOCKERHUB_USERNAME/hive-mind # Change this to your username- Push changes to the
mainbranch - Go to Actions tab in your GitHub repository
- Find the "Docker Build and Publish" workflow
- Check that it completes successfully
- Verify the image appears on hub.docker.com/r/YOUR_USERNAME/hive-mind
- On Pull Requests: The workflow tests building the Docker image without publishing
- On Main Branch: The workflow builds and publishes to Docker Hub with the
latesttag - On Version Tags: The workflow publishes with semantic version tags (e.g.,
v0.37.0,0.37,0)
Build fails with authentication error:
- Verify
DOCKERHUB_USERNAMEmatches your Docker Hub username exactly - Regenerate
DOCKERHUB_TOKENand update the secret
Image published but can't pull:
- Ensure the repository on Docker Hub is public (or you're authenticated)
- Check hub.docker.com → Your repositories → hive-mind → Settings → Make Public
Build succeeds but image doesn't appear:
- Check you're pushing to the
mainbranch (pull requests only test, don't publish) - Verify the workflow ran in the Actions tab
- Check Docker Hub rate limits haven't been exceeded
- Each container maintains its own isolated authentication
- No credentials are shared between containers
- No credentials are stored in the Docker image itself
- Authentication happens inside the container after it starts
- Each GitHub/Claude account can have its own container instance
- Docker Hub access tokens should be stored only as GitHub Secrets, never committed to the repository