Convert to Docker
This skill migrates NanoClaw from Apple Container (macOS-only) to Docker for cross-platform support (macOS and Linux).
What this changes:
- Container runtime: Apple Container → Docker
- Mount syntax:
--mount type=bind,...,readonly → -v path:path:ro
- Startup check:
container system status → docker info
- Build commands:
container build/run → docker build/run
What stays the same:
- Dockerfile (already Docker-compatible)
- Agent runner code
- Mount security/allowlist validation
- All other functionality
Prerequisites
Verify Docker is installed before starting:
bash
1docker --version && docker info >/dev/null 2>&1 && echo "Docker ready" || echo "Install Docker first"
If Docker is not installed:
1. Update Container Runner
Edit src/container-runner.ts:
typescript
1// Before:
2 * Spawns agent execution in Apple Container and handles IPC
3
4// After:
5 * Spawns agent execution in Docker container and handles IPC
typescript
1// Before:
2 // Apple Container only supports directory mounts, not file mounts
3
4// After:
5 // Docker bind mounts work with both files and directories
typescript
1// Before:
2 // Environment file directory (workaround for Apple Container -i env var bug)
3
4// After:
5 // Environment file directory (keeps credentials out of process listings)
1d. Update buildContainerArgs function
Replace the entire function with Docker mount syntax:
typescript
1function buildContainerArgs(mounts: VolumeMount[]): string[] {
2 const args: string[] = ['run', '-i', '--rm'];
3
4 // Docker: -v with :ro suffix for readonly
5 for (const mount of mounts) {
6 if (mount.readonly) {
7 args.push('-v', `${mount.hostPath}:${mount.containerPath}:ro`);
8 } else {
9 args.push('-v', `${mount.hostPath}:${mount.containerPath}`);
10 }
11 }
12
13 args.push(CONTAINER_IMAGE);
14
15 return args;
16}
1e. Update spawn command (around line 204)
typescript
1// Before:
2 const container = spawn('container', containerArgs, {
3
4// After:
5 const container = spawn('docker', containerArgs, {
2. Update Startup Check
Edit src/index.ts:
2a. Replace the container system check function
Find ensureContainerSystemRunning() and replace entirely with:
typescript
1function ensureDockerRunning(): void {
2 try {
3 execSync('docker info', { stdio: 'pipe', timeout: 10000 });
4 logger.debug('Docker daemon is running');
5 } catch {
6 logger.error('Docker daemon is not running');
7 console.error('\n╔════════════════════════════════════════════════════════════════╗');
8 console.error('║ FATAL: Docker is not running ║');
9 console.error('║ ║');
10 console.error('║ Agents cannot run without Docker. To fix: ║');
11 console.error('║ macOS: Start Docker Desktop ║');
12 console.error('║ Linux: sudo systemctl start docker ║');
13 console.error('║ ║');
14 console.error('║ Install from: https://docker.com/products/docker-desktop ║');
15 console.error('╚════════════════════════════════════════════════════════════════╝\n');
16 throw new Error('Docker is required but not running');
17 }
18}
2b. Update the function call in main()
typescript
1// Before:
2 ensureContainerSystemRunning();
3
4// After:
5 ensureDockerRunning();
3. Update Build Script
Edit container/build.sh:
3a. Update build command (around line 15-16)
bash
1# Before:
2# Build with Apple Container
3container build -t "${IMAGE_NAME}:${TAG}" .
4
5# After:
6# Build with Docker
7docker build -t "${IMAGE_NAME}:${TAG}" .
3b. Update test command (around line 23)
bash
1# Before:
2echo " echo '{...}' | container run -i ${IMAGE_NAME}:${TAG}"
3
4# After:
5echo " echo '{...}' | docker run -i ${IMAGE_NAME}:${TAG}"
4. Update Documentation
Update references in documentation files:
| File | Find | Replace |
|---|
CLAUDE.md | "Apple Container (Linux VMs)" | "Docker containers" |
README.md | "Apple containers" | "Docker containers" |
README.md | "Apple Container" | "Docker" |
README.md | Requirements section | Update to show Docker instead |
docs/REQUIREMENTS.md | "Apple Container" | "Docker" |
docs/SPEC.md | "APPLE CONTAINER" | "DOCKER CONTAINER" |
docs/SPEC.md | All Apple Container references | Docker equivalents |
Key README.md updates:
Requirements section:
markdown
1## Requirements
2
3- macOS or Linux
4- Node.js 20+
5- [Claude Code](https://claude.ai/download)
6- [Docker](https://docker.com/products/docker-desktop)
FAQ - "Why Docker?":
markdown
1**Why Docker?**
2
3Docker provides cross-platform support (macOS and Linux), a large ecosystem, and mature tooling. Docker Desktop on macOS uses a lightweight Linux VM similar to other container solutions.
FAQ - "Can I run this on Linux?":
markdown
1**Can I run this on Linux?**
2
3Yes. NanoClaw uses Docker, which works on both macOS and Linux. Just install Docker and run `/setup`.
5. Update Skills
5a. Update .claude/skills/setup/SKILL.md
Replace Section 2 "Install Apple Container" with Docker installation:
markdown
1## 2. Install Docker
2
3Check if Docker is installed and running:
4
5\`\`\`bash
6docker --version && docker info >/dev/null 2>&1 && echo "Docker is running" || echo "Docker not running or not installed"
7\`\`\`
8
9If not installed or not running, tell the user:
10> Docker is required for running agents in isolated environments.
11>
12> **macOS:**
13> 1. Download Docker Desktop from https://docker.com/products/docker-desktop
14> 2. Install and start Docker Desktop
15> 3. Wait for the whale icon in the menu bar to stop animating
16>
17> **Linux:**
18> \`\`\`bash
19> curl -fsSL https://get.docker.com | sh
20> sudo systemctl start docker
21> sudo usermod -aG docker $USER # Then log out and back in
22> \`\`\`
23>
24> Let me know when you've completed these steps.
25
26Wait for user confirmation, then verify:
27
28\`\`\`bash
29docker run --rm hello-world
30\`\`\`
Update build verification:
markdown
1Verify the build succeeded:
2
3\`\`\`bash
4docker images | grep nanoclaw-agent
5echo '{}' | docker run -i --entrypoint /bin/echo nanoclaw-agent:latest "Container OK" || echo "Container build failed"
6\`\`\`
Update troubleshooting section to reference Docker commands.
5b. Update .claude/skills/debug/SKILL.md
Replace all container commands with docker equivalents:
| Before | After |
|---|
container run | docker run |
container system status | docker info |
container builder prune | docker builder prune |
container images | docker images |
--mount type=bind,source=...,readonly | -v ...:ro |
Update the architecture diagram header:
Host (macOS/Linux) Container (Docker)
6. Build and Verify
After making all changes:
bash
1# Compile TypeScript
2npm run build
3
4# Build Docker image
5./container/build.sh
6
7# Verify image exists
8docker images | grep nanoclaw-agent
7. Test the Migration
7a. Test basic container execution
bash
1echo '{}' | docker run -i --entrypoint /bin/echo nanoclaw-agent:latest "Container OK"
7b. Test readonly mounts
bash
1mkdir -p /tmp/test-ro && echo "test" > /tmp/test-ro/file.txt
2docker run --rm --entrypoint /bin/bash -v /tmp/test-ro:/test:ro nanoclaw-agent:latest \
3 -c "cat /test/file.txt && touch /test/new.txt 2>&1 || echo 'Write blocked (expected)'"
4rm -rf /tmp/test-ro
Expected: Read succeeds, write fails with "Read-only file system".
7c. Test read-write mounts
bash
1mkdir -p /tmp/test-rw
2docker run --rm --entrypoint /bin/bash -v /tmp/test-rw:/test nanoclaw-agent:latest \
3 -c "echo 'test write' > /test/new.txt && cat /test/new.txt"
4cat /tmp/test-rw/new.txt && rm -rf /tmp/test-rw
Expected: Both operations succeed.
7d. Full integration test
bash
1npm run dev
2# Send @AssistantName hello via WhatsApp
3# Verify response received
Troubleshooting
Docker not running:
- macOS: Start Docker Desktop from Applications
- Linux:
sudo systemctl start docker
- Verify:
docker info
Permission denied on Docker socket (Linux):
bash
1sudo usermod -aG docker $USER
2# Log out and back in
Image build fails:
bash
1# Clean rebuild
2docker builder prune -af
3./container/build.sh
Container can't write to mounted directories:
Check directory permissions on the host. The container runs as uid 1000.
Summary of Changed Files
| File | Type of Change |
|---|
src/container-runner.ts | Mount syntax, spawn command, comments |
src/index.ts | Startup check function |
container/build.sh | Build and run commands |
CLAUDE.md | Quick context |
README.md | Requirements, FAQ |
docs/REQUIREMENTS.md | Architecture references |
docs/SPEC.md | Architecture diagram, tech stack |
.claude/skills/setup/SKILL.md | Installation instructions |
.claude/skills/debug/SKILL.md | Debug commands |