Get from zero to your first AI sandbox in under 5 minutes.
Before starting, ensure you have:
| Requirement | Version | Check Command |
|---|---|---|
| Docker | Desktop 4.50+ or Engine 24.0+ | docker --version |
| Bash shell | 4.0+ | echo "${BASH_VERSION}" |
| Git | Any | git --version |
Shell note: ContainAI requires bash 4.0+. If you use zsh, fish, or another shell, run
bashfirst. macOS ships with bash 3.2; install a newer version via Homebrew (brew install bash).
git clone https://github.com/novotnyllc/containai.git
cd containaiVerify:
ls src/containai.sh
# Should show: src/containai.shsource src/containai.shVerify:
cai --help | head -3
# Should show:
# ContainAI - Run AI coding agents in a secure Docker sandbox
#
# Usage: containai [subcommand] [options]Note: You must source the script (not execute it) to add the
caicommand to your shell. Add this to your~/.bashrcfor persistence.
ContainAI follows the Unix Rule of Silence: commands produce no output on success by default.
| Output Type | Default | --verbose |
|---|---|---|
| Info/step messages | Hidden | Shown |
| Warnings | Shown (stderr) | Shown |
| Errors | Shown (stderr) | Shown |
Enable verbose output:
# Per-command flag
cai --verbose doctor
# Environment variable (persistent)
export CONTAINAI_VERBOSE=1Precedence: --verbose flag > CONTAINAI_VERBOSE environment variable.
Exceptions: cai doctor, cai help, and cai version always produce output regardless of verbosity settings.
Run the doctor command to detect your system's capabilities:
cai doctorWhat to look for:
| Output | Meaning | Action |
|---|---|---|
Sysbox: [OK] |
Sysbox runtime configured | Ready to go! |
Sysbox: [ERROR] |
No isolation available | Install Sysbox (see below) |
%%{init: {'theme': 'base', 'themeVariables': {
'primaryColor': '#1a1a2e',
'primaryTextColor': '#ffffff',
'primaryBorderColor': '#16213e',
'secondaryColor': '#0f3460',
'tertiaryColor': '#1a1a2e',
'lineColor': '#a0a0a0',
'textColor': '#ffffff',
'background': '#0d1117'
}}}%%
flowchart TD
doctor["cai doctor"]
doctor --> sysbox["Sysbox<br/>[OK]"]
sysbox --> ready["Ready to run!<br/>cai"]
doctor -.->|not OK| install
subgraph fallback["If Sysbox not available"]
install["Install Sysbox<br/>WSL2/macOS: Run 'cai setup'<br/>Native Linux: See Sysbox docs"]
end
Navigate to a project directory (or stay in containai for testing):
# Optional: go to your project
cd /path/to/your/project
# Start the sandbox
caiVerify:
Starting new sandbox container...
# or
Attaching to running container...
You should see the Claude agent interface (or a login prompt if not yet authenticated).
If Claude prompts you to log in, follow the authentication flow. If you need to authenticate manually:
- Open a new terminal
- Run
cai shellto get a bash prompt inside the running container - Run
claude loginand follow the prompts
Credentials are stored in the sandbox's persistent data volume and persist across container restarts.
Verify (from cai shell):
claude --version
# Should show Claude CLI version without authentication errorsUsing a different agent? Start the sandbox with
cai --agent gemini(on your host), then authenticate viacai shellandgemini login.
When you ran cai, ContainAI:
- Detected isolation mode - Checked for Sysbox runtime availability
- Created a named container - Based on your git repo and branch (e.g.,
myproject-main) - Mounted your workspace - Your current directory is available at
/workspaceinside the container - Created a data volume -
containai-datastores your agent credentials and plugins - Started the AI agent - Claude (or your configured agent) is ready to use
%%{init: {'theme': 'base', 'themeVariables': {
'primaryColor': '#1a1a2e',
'primaryTextColor': '#ffffff',
'primaryBorderColor': '#16213e',
'secondaryColor': '#0f3460',
'tertiaryColor': '#1a1a2e',
'lineColor': '#a0a0a0',
'textColor': '#ffffff',
'background': '#0d1117'
}}}%%
flowchart TB
subgraph host["Your Host Machine"]
subgraph runtime["Sysbox Runtime"]
subgraph sandbox["ContainAI Sandbox"]
workspace["/workspace<br/>Your project directory"]
data["/mnt/agent-data<br/>Persistent credentials/plugins"]
agent["Claude/Gemini agent running"]
end
end
end
| Command | Description |
|---|---|
cai |
Start/attach to sandbox |
cai --restart |
Force recreate container (e.g., after config changes) |
cai doctor |
Check system capabilities |
cai shell |
Open bash shell in running sandbox |
cai stop --all |
Stop all ContainAI containers |
ContainAI supports user templates to customize your container environment. Templates let you install additional tools, languages, or startup scripts that persist across container recreations.
For quick customizations that don't require rebuilding images, see:
- Startup Hooks - Run scripts at container start
- Network Policies - Control egress traffic
Templates are stored in ~/.config/containai/templates/. The default template is installed during first use:
~/.config/containai/templates/
└── default/
└── Dockerfile # Your customizable Dockerfile
Open the default template and add your customizations:
# View/edit your template
${EDITOR:-nano} ~/.config/containai/templates/default/DockerfileThe template Dockerfile includes comments showing how to:
- Install system packages (apt-get)
- Add Node/Python/Rust packages
- Create startup scripts
# Use the default template (automatic)
cai
# Use a specific template
cai --template my-custom
# Rebuild container with template changes
cai --freshTo run scripts when the container starts, create a systemd service. In Dockerfiles, you must use the symlink pattern instead of systemctl enable (systemd is not running during docker build):
# Create your startup script
COPY my-startup.sh /opt/containai/startup/my-startup.sh
RUN chmod +x /opt/containai/startup/my-startup.sh
# Create the systemd service file
COPY my-startup.service /etc/systemd/system/my-startup.service
# Enable using symlink (NOT systemctl enable)
RUN ln -sf /etc/systemd/system/my-startup.service \
/etc/systemd/system/multi-user.target.wants/my-startup.serviceExample service file (my-startup.service):
[Unit]
Description=My Custom Startup Script
After=containai-init.service
[Service]
Type=oneshot
ExecStart=/opt/containai/startup/my-startup.sh
User=agent
[Install]
WantedBy=multi-user.targetWhen customizing templates, avoid these common mistakes:
| What NOT to do | Why |
|---|---|
| Override ENTRYPOINT | systemd must be PID 1 for services to work |
| Override CMD | Required for systemd startup |
| Change USER to non-agent | Permissions will break (agent is UID 1000) |
Use systemctl enable |
Fails during docker build; use symlink pattern |
If your template has issues, recover it with:
# Check template status
cai doctor
# Restore default template from repo
cai doctor fix templateSee Configuration Reference for template configuration options.
- Container lifecycle - See Container Lifecycle for details on creation, stopping, and garbage collection
- Configure ContainAI - See the Technical README for volume, naming, and configuration options
- Troubleshoot issues - See Troubleshooting for common problems
- Customize your container - See Configuration Reference for template options
- Security model - See SECURITY.md for security guarantees and threat model
# Add to ~/.bashrc for permanent access
echo 'source /path/to/containai/src/containai.sh' >> ~/.bashrc
# Or source manually each session
source src/containai.sh| Platform | Shell | Notes |
|---|---|---|
| Linux | bash | Native support |
| WSL2 | bash | Native support |
| macOS | zsh default | Run bash first, then source |
| macOS | bash | Direct support |
- Docker Engine: 24.0+ with Sysbox runtime
- Git: any recent version
- Bash: 4.0+ (macOS default is 3.2; use
brew install bash)
For frequent agent usage, add wrapper functions to your ~/.bashrc:
# Agent wrapper functions (add to ~/.bashrc)
claude() { CONTAINAI_AGENT=claude cai -- "$@"; }
gemini() { CONTAINAI_AGENT=gemini cai -- "$@"; }Note: ContainAI requires bash 4.0+, so these functions belong in
~/.bashrc, not~/.zshrc. If you use zsh as your default shell, runbashfirst (as noted in the prerequisites), then these functions will be available.
claude "Fix the bug in main.py"
gemini "Explain this code"Always quote arguments with spaces or special characters:
# Good - quoted argument passed as single string
claude "What does this function do?"
# Bad - splits into multiple arguments
claude What does this function do?Set the agent for the current session:
export CONTAINAI_AGENT=claude
cai -- "Fix the bug"Or configure it permanently:
cai config set agent.default claude