Dev Container Documentation
May 30, 2026 ยท View on GitHub
The cuti dev container provides a fully configured development environment with cuti, Claude Code mode as the default, and an explicit OpenClaw mode for the OpenClaw runtime plus configured add-ons.
Quick Start
# Start an interactive container session (works from any directory)
cuti container
# Run a specific command in the container
cuti container "cuti web"
cuti container "claude 'Explain this project'"
Key Features
๐ Persistent Claude Authentication
- Separate Linux credentials in
~/.cuti/claude-linux/ - No conflicts with macOS Keychain
- One-time authentication for all containers
- ๐ Complete Authentication Guide
๐ค Agent Providers
- Claude Code mode is enabled by default for
cuti container - OpenClaw mode is opt-in with
cuti container --openclaworcuti container --claw - OpenClaw mode starts with OpenClaw only; add-ons are read from
cuti providers enable ... - Provider-specific auth, config, and skills directories are mounted automatically
- Provider CLIs are installed through their current standalone/native install paths at container startup
- Claude Code mode refreshes Claude Code into the persistent provider runtime on each
cuti containerstartup - Hermes is available as an experimental provider track in cuti
๐ฏ Smart Container Selection
-
Universal Container (
cuti-dev-universal): Used when running from any project directory- Installs cuti from PyPI via
uv tool install - Perfect for using cuti with any project
- Pre-configured with all tools
- Installs cuti from PyPI via
-
Development Container (
cuti-dev-cuti): Used when running from cuti source directory- Installs cuti from local source code
- Ideal for cuti development and testing
- Includes development dependencies
๐จ Custom Environment
- Custom prompt:
cuti:~/path $for clear container identification - Pre-configured with oh-my-zsh for better terminal experience
- All cuti commands available immediately
- Python 3.11, Node.js 20, and common development tools pre-installed
๐ณ Docker-in-Docker Support
- Docker CLI installed in container (not the daemon)
- Host Docker socket mounted at
/var/run/docker.sock - Automatic sudo wrapper if needed for permissions
- Run Docker commands that execute on host's Docker daemon
- Build images, run containers, use docker-compose - all from within the container
- Perfect for projects that need containerization during development
Prerequisites
macOS
# Install Colima (recommended Docker alternative for Mac)
brew install colima
# Start Colima
colima start
# Or use Docker Desktop
# Download from https://www.docker.com/products/docker-desktop
Linux
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker
Windows
- Install Docker Desktop from https://www.docker.com/products/docker-desktop
- Or use WSL2 with Docker
Usage Examples
Interactive Development
# Start container from any project
cd ~/my-project
cuti container
# You'll see the custom prompt:
# cuti:/workspace $
# Inside container, all commands work:
cuti web # Start read-only ops console (accessible at http://localhost:8000)
cuti cli # Start interactive CLI
cuti agent list # List available agents
claude --help # Use Claude CLI (already authenticated)
Non-Interactive Commands
# Run cuti commands
cuti container "cuti add 'Review this code and suggest improvements'"
cuti container "cuti start"
cuti container "cuti status"
# Run Claude directly
cuti container "claude 'What does this project do?'"
# Add providers to the default Claude Code container mode
cuti providers enable codex
cuti providers enable opencode
cuti providers enable hermes
cuti container --rebuild
cuti container "codex --version && opencode --version && hermes version"
# Start OpenClaw mode explicitly
cuti container --openclaw
# OpenClaw add-ons come from explicit provider config
cuti providers enable claude
cuti providers enable codex
cuti container --openclaw "openclaw --version && claude --version && codex --version"
# Run Python scripts
cuti container "python script.py"
Ops Console
# Start the read-only ops console in container (accessible from host)
cuti container "cuti web"
# Then open http://localhost:8000 in your browser
# Run development servers
cuti container "npm run dev"
cuti container "python manage.py runserver"
Claude CLI Configuration
The container comes with Claude CLI pre-configured for optimal usage:
Automatic Permissions Bypass
- Built-in Alias:
claudeautomatically includes--dangerously-skip-permissions - No need to manually add the flag for every command
- Works seamlessly in the containerized environment
# These commands are equivalent in the container:
claude "Explain this code" # Uses alias (recommended)
claude --dangerously-skip-permissions "Explain this code" # Explicit flag (not needed)
Why This Matters
- The
--dangerously-skip-permissionsflag is required in containers - Without it, Claude may fail with permission errors
- The alias ensures Claude always works correctly
- Simplifies usage and prevents common errors
Rebuild to Apply Updates
If you're using an older container image, rebuild to get the alias:
cuti container --rebuild
๐ค Agent Providers
Claude Code mode is the default for cuti container. OpenClaw mode is explicit and does not join the default container just because OpenClaw has been enabled in provider config:
cuti providers list
cuti providers doctor
# Default Claude Code mode. Claude Code is refreshed for subsequent containers.
cuti container
# Explicit OpenClaw mode.
cuti container --openclaw
# OpenClaw mode add-ons.
cuti providers enable claude
cuti providers enable codex
cuti providers enable opencode
cuti providers enable hermes
cuti container --openclaw
cuti providers auth claude --login
qt-openclaw onboard
qt-openclaw up
cuti providers update codex
cuti providers update openclaw
cuti providers update hermes
The standard cloud profile exports provider metadata into the container:
CUTI_CONTAINER_MODEisclaude-codeoropenclawCUTI_AGENT_PROVIDERScontains the enabled provider IDsCUTI_PRIMARY_AGENT_PROVIDERisclaudein Claude Code mode andopenclawin OpenClaw modeCODEX_HOME,OPENCLAW_STATE_DIR,OPENCLAW_CONFIG_PATH,OPENCLAW_PREFIX,HERMES_HOME,XDG_CONFIG_HOME, andXDG_DATA_HOMEare set for provider state
Provider-specific mounts are added automatically when the provider is enabled:
| Provider | Host Path | Container Path | Purpose |
|---|---|---|---|
| Claude | ~/.cuti/claude-linux/ | /home/cuti/.claude-linux | Linux Claude credentials |
| Provider runtimes | ~/.cuti/provider-runtimes/ | /home/cuti/.cuti-providers | Persistent provider CLI installs |
| Claude | ~/.claude/ | /home/cuti/.claude-macos | macOS config reference (read-only) |
| Codex | ~/.codex/ | /home/cuti/.codex | Auth, config, skills |
| OpenCode | ~/.opencode/ | /home/cuti/.opencode | CLI install and provider state |
| OpenCode | ~/.config/opencode/ | /home/cuti/.config/opencode | Config |
| OpenCode | ~/.local/share/opencode/ | /home/cuti/.local/share/opencode | Data |
| OpenClaw | ~/.openclaw/ | /home/cuti/.openclaw | Gateway config, credentials, agents, plugins, browser, voice-call, logs, media |
| Hermes Agent | ~/.hermes/ | /home/cuti/.hermes | Config, .env, memory, skills, sessions, gateway state |
| Hermes/OpenClaw migration | ~/.openclaw/ | /home/cuti/.openclaw | Read-only migration source when Hermes is enabled without OpenClaw |
| Hermes Claude auth reuse | ~/.claude/ | /home/cuti/.claude | Read-only Claude Code credentials for Hermes native Anthropic auth |
| Shared agent files | ~/.agents/ | /home/cuti/.agents | Shared provider skills/prompts |
Install behavior is provider-specific:
- Claude uses the official native installer
- Codex uses the official standalone installer
- OpenCode uses the official install script
- OpenClaw uses the official local-prefix installer when available, falls back to npm prefix install, stores the CLI under
~/.cuti/provider-runtimes/openclaw/, and runsopenclaw doctor --non-interactiveafter install/update/bootstrap - Hermes Agent uses the official NousResearch installer with
HERMES_HOME=/home/cuti/.hermes
OpenClaw-specific notes:
qt-openclaw ...andqt-OpenClaw ...run the same dedicated OpenClaw command group from the host.qt-openclaw onboardrunsopenclaw onboard --install-daemonin the container, then runsopenclaw doctor --non-interactive.qt-openclaw upruns onboarding if no OpenClaw state is detected, runs doctor, then starts the gateway in the foreground.- Channel, browser, plugin, voice-call, and dashboard surfaces are exposed as direct wrappers:
qt-openclaw channels-login,qt-openclaw channels ...,qt-openclaw browser ...,qt-openclaw plugins ...,qt-openclaw voice-setup,qt-openclaw voicecall ..., andqt-openclaw dashboard .... - Source-backed OpenClaw command families are also exposed directly: setup/config/configure, backup/reset/uninstall, message/agent/agents, status/health/sessions, tasks/flows/cron, models/infer/capability, ACP/MCP, approvals/exec-policy, nodes/devices/node, sandbox, TUI/chat/terminal, DNS/docs/proxy, hooks/webhooks, QR/pairing/directory, security/secrets/skills, update/completion, memory, and wiki.
- Future plugin command roots remain supported through
qt-openclaw run <command> ..., which forwards raw arguments to the installed OpenClaw CLI after cuti has enabled the provider and mounted state. - Managed OpenClaw state persists under
~/.openclaw; the CLI/runtime install persists separately under~/.cuti/provider-runtimes/openclaw. - cuti sets OpenClaw's container environment to the mounted state tree:
OPENCLAW_HOME=/home/cuti,OPENCLAW_STATE_DIR=/home/cuti/.openclaw,OPENCLAW_CONFIG_PATH=/home/cuti/.openclaw/openclaw.json,OPENCLAW_OAUTH_DIR=/home/cuti/.openclaw/credentials,OPENCLAW_PREFIX=/home/cuti/.cuti-providers/openclaw,NPM_CONFIG_PREFIX=/home/cuti/.cuti-providers/openclaw,npm_config_prefix=/home/cuti/.cuti-providers/openclaw, andPLAYWRIGHT_BROWSERS_PATH=/home/cuti/.openclaw/browser/browsers. - Containerized browser automation works best with OpenClaw-managed or remote-CDP browser profiles. Attaching to an already signed-in host browser still depends on host-local browser access.
- Voice-call setup installs/enables the official
@openclaw/voice-callplugin, but telephony providers still require their own credentials and reachable webhook/tunnel setup.
Hermes-specific notes:
- Hermes is marked experimental in
cuti providers list/status cuti providers auth hermes --loginrunshermes setup, which follows the upstream first-run wizard and can detect OpenClaw state for migrationcuti providers update hermesfollows Hermes' nativehermes updateflow inside the container, so upstream dependency refresh, config checks, and bundled skill sync happen the same way Hermes documents them- If
~/.openclawexists, runhermes claw migrate --dry-runinside the container to preview migration of OpenClaw persona, memory, skills, messaging settings, API keys, and workspace instructions before applying it - Hermes project context uses
.hermes.md,HERMES.md,AGENTS.md, andCLAUDE.md; durable personality belongs in~/.hermes/SOUL.md - Hermes profiles persist under
~/.hermes/profiles, and cuti recreates the lightweight~/.local/bin/<profile>wrappers on container startup so profile aliases continue to work after container restarts
Host-side provider commands:
cuti providers listshows selection plus setup summarycuti providers status <provider>shows detailed host/container statecuti providers doctorchecks readiness across providerscuti providers auth <provider> --loginlaunches the provider's interactive setup flow inside the containercuti providers update <provider>refreshes that provider in persistent provider runtime storage and updates active cuti cloud containers in place
Docker-in-Docker Usage
# Build Docker images inside the container
cuti container "docker build -t myapp ."
# Run containers from within the container
cuti container "docker run -d redis:latest"
cuti container "docker ps"
# Use docker-compose
cuti container "docker-compose up -d"
# Interactive container with Docker support
cuti container
# Inside container:
docker images
docker run hello-world
docker-compose --version
Architecture
Container Images
cuti-dev-universal (Default for most projects)
- Base:
python:3.11-bullseye - Cuti: Installed from PyPI via
uv tool install cuti - Agent providers: Claude preinstalled, other enabled providers installed at runtime
- Docker CLI: Installed for Docker-in-Docker support
- Tools: git, zsh, ripgrep, fd-find, bat, jq, curl, wget
- Python: uv package manager, pytest, httpx, fastapi, uvicorn
- Node.js: v20 with latest npm
- Shell: Zsh with oh-my-zsh and custom cuti prompt
cuti-dev-cuti (For cuti development)
- Same base as universal
- Cuti: Installed from local source code
- Includes all cuti development dependencies
- Used automatically when running from cuti source directory
Volume Mounts
The container automatically mounts:
| Host Path | Container Path | Purpose |
|---|---|---|
| Current directory | /workspace | Your project files |
~/.cuti/ | /home/cuti/.cuti-shared | Shared cuti config and account state |
/var/run/docker.sock | /var/run/docker.sock | Docker socket for Docker-in-Docker |
Additional provider mounts are added only for enabled providers.
Environment Variables
Automatically set in container:
CUTI_IN_CONTAINER=true- Indicates running in containerCUTI_AGENT_PROVIDERS- Comma-separated enabled providersCUTI_PRIMARY_AGENT_PROVIDER- Primary provider for prompts/help textCLAUDE_CONFIG_DIR=/home/cuti/.claude-linux- Linux Claude config locationCLAUDE_DANGEROUSLY_SKIP_PERMISSIONS=true- Skip permission checksCODEX_HOME=/home/cuti/.codex- Codex state rootHERMES_HOME=/home/cuti/.hermes- Hermes Agent config/state rootHERMES_INSTALL_DIR=/home/cuti/.hermes/hermes-agent- persisted Hermes Agent installationXDG_CONFIG_HOME=/home/cuti/.config- Shared XDG config rootXDG_DATA_HOME=/home/cuti/.local/share- Shared XDG data rootPYTHONUNBUFFERED=1- Python unbuffered outputTERM=xterm-256color- Color terminal supportANTHROPIC_API_KEY- Loaded from environment or file if available
Network Configuration
Container runs with --network host for easy service access:
- Port 8000: cuti ops console
- Port 8080: Alternative web services
- Port 3000: Frontend dev servers
- Port 5000: Flask/FastAPI apps
- Port 5173: Vite dev server
Project Type Detection
When generating dev containers, cuti automatically detects:
| Project Type | Detection Files | Additional Tools |
|---|---|---|
| Python | pyproject.toml, requirements.txt | pytest, black, ruff, mypy |
| JavaScript | package.json | yarn, pnpm, typescript, nodemon |
| Full-stack | Both Python + JS files | All Python + JS tools |
| Ruby | Gemfile | Ruby, Bundler |
| Go | go.mod | Go 1.21+ |
| Rust | Cargo.toml | Rust, Cargo |
| General | None of above | Basic dev tools |
Troubleshooting
Docker Socket Issues After Container Exit (Fixed)
Previous versions had an issue where exiting the container would break the Docker socket connection on macOS with Colima. This has been fixed by:
- Using
--initflag for proper signal handling - Installing only Docker CLI (not the full Docker engine)
- Adding proper signal traps for clean exit
If you experience this issue with an older version, restart Colima:
colima restart
Container Won't Start
- Check Docker/Colima is running:
# For Colima
colima status
# For Docker
docker version
- Start if needed:
# Colima (macOS)
colima start
# Docker Desktop - start from application
- Verify image exists:
docker images | grep cuti-dev
Claude Authentication
โ Solved: Claude authentication persists across all containers using separate Linux credentials.
Quick Setup:
- Run
cuti container - Inside container, run
claude login(first time only) - Authentication persists for all future containers
How it works:
- Linux credentials stored in
~/.cuti/claude-linux/ - macOS credentials remain in Keychain (no conflicts)
- Settings copied from macOS automatically
๐ For detailed setup and troubleshooting, see Claude Container Authentication Guide
Cuti Not Found in Container
- Check which container is being used:
docker images | grep cuti-dev
- Force rebuild:
# Remove old images
docker rmi cuti-dev-universal cuti-dev-cuti
# Rebuild
cuti container
- Verify installation:
docker run --rm cuti-dev-universal which cuti
docker run --rm cuti-dev-universal cuti --help
"input device is not a TTY" Error
This occurs in non-interactive environments (like Claude Code). The container automatically detects and handles this - no action needed.
Permission Issues
The container runs as root for simplicity. To fix file ownership after container use:
# On host after exiting container
sudo chown -R $(whoami) .
Port Already in Use
If port 8000 is already in use:
# Find what's using the port
lsof -i :8000
# Kill the process or use different port
cuti container "cuti web --port 8001"
Docker-in-Docker Access
How Docker access works in the container:
- The container automatically detects if Docker socket requires sudo
- If needed, creates a wrapper script that adds sudo automatically
- You can use
dockercommands normally - sudo is handled transparently
Docker commands work normally:
# Inside container - these all work
docker ps
docker build -t myapp .
docker-compose up -d
If Docker still shows "permission denied":
- The container should auto-configure this, but if not:
sudo docker ps # Test with sudo - Exit and restart the container - the wrapper will be created
Docker daemon not accessible:
- Ensure Docker/Colima is running on your host machine
- On macOS with Colima:
colima statusandcolima startif needed - On Linux:
sudo systemctl status docker
Colima Specific Issues (macOS)
Colima won't start:
# Stop and clean
colima stop -f
colima delete
# Start with specific settings
colima start --arch aarch64 --vm-type vz --cpu 2 --memory 4
Docker commands fail after Colima start:
# Check Docker context
docker context ls
# Set to use Colima
docker context use colima
Advanced Usage
Custom Dockerfile
After generating dev container files:
# Generate container configuration
cuti devcontainer generate
# Edit .devcontainer/Dockerfile to add custom tools
# Then rebuild
docker build -t my-custom-cuti -f .devcontainer/Dockerfile .
VS Code Integration
- Install "Dev Containers" extension
- Run
cuti devcontainer generatein your project - Command Palette: "Dev Containers: Reopen in Container"
- VS Code uses the generated configuration
Running Services
# Database in background
cuti container "docker run -d postgres:15"
# Redis
cuti container "docker run -d redis:7"
# Your app
cuti container "cuti web"
Persistent Data
Create named volumes for persistence:
# Create volume
docker volume create myproject-data
# Use in container
docker run -v myproject-data:/data ...
Command Reference
Main Commands
# Interactive container
cuti container
# Run specific command
cuti container "COMMAND"
# Generate dev container files
cuti devcontainer generate [--type TYPE]
# Clean up
cuti devcontainer clean
Docker Management
# List cuti images
docker images | grep cuti
# Remove cuti images
docker rmi cuti-dev-universal cuti-dev-cuti
# Clean all Docker resources
docker system prune -a
# Check container logs
docker logs [container-name]
# Execute command in running container
docker exec -it [container-name] bash
Implementation Details
Container Build Process
- Detection: Determines if running from cuti source or other project
- Image Selection: Chooses
cuti-dev-universalorcuti-dev-cuti - Build Check: Checks if image exists, builds if missing
- Mount Setup: Configures volume mounts for project and configs
- Environment: Sets environment variables
- Execution: Runs container with appropriate flags
File Locations
- Dockerfile templates:
src/cuti/services/devcontainer.py - Universal Dockerfile:
.devcontainer/Dockerfile.universal - Development Dockerfile:
.devcontainer/Dockerfile - Generated files:
.devcontainer/in your project
Security Considerations
- Containers do not run with
--privileged - Claude automatically uses
--dangerously-skip-permissionsvia alias (see Claude CLI Configuration) - Config directories mounted with appropriate permissions
- No telemetry or external connections
- Local-first approach
Contributing
To improve dev container support:
- Edit
src/cuti/services/devcontainer.py - Update Dockerfile templates
- Test with both container types:
# Test universal container cd ~/some-project cuti container # Test development container cd ~/cuti-source cuti container - Submit PR with test results
Related Documentation
- Todo System - Task management in cuti
- Rate Limit Handling - How cuti handles API limits
- Main README - Project overview
License
Part of the cuti project - MIT License