Pi Agent — Docker Sandbox Guide (macOS)
Run Pi (pi.dev) per-project inside Docker. Pi can only touch your project folder — it has no access to your Mac’s root, home directory, or any other path you haven’t explicitly mounted.
What’s in this folder
pi-docker/
├── Dockerfile — The sandbox image definition
├── build.sh — One-time image build script
├── run.sh — Per-project launcher
└── GUIDE.md — This file
Prerequisites
-
Docker Desktop for Mac — download from https://docker.com/products/docker-desktop Install it, open it, and make sure the whale icon appears in your menu bar.
-
An API key for your LLM provider (Anthropic, OpenAI, OpenRouter, etc.) Add it to your shell profile (
~/.zshrc) so run.sh picks it up automatically:# ~/.zshrc export ANTHROPIC_API_KEY="sk-ant-..." # or export OPENAI_API_KEY="sk-..." # or export OPENROUTER_API_KEY="sk-or-..."Then reload:
source ~/.zshrc
One-time setup
1. Put the pi-docker folder somewhere permanent
mv pi-docker ~/pi-docker
2. Make the scripts executable
chmod +x ~/pi-docker/build.sh ~/pi-docker/run.sh
3. Build the Docker image (once)
~/pi-docker/build.sh
This downloads Node 22 and installs Pi inside the image. Takes ~1 minute.
You only need to run this once (or after updating Dockerfile).
Using Pi in a project
Navigate to any project and launch Pi:
cd ~/projects/my-app
~/pi-docker/run.sh
Inside the container, Pi sees:
/workspace/ ← your project (read + write)
/home/agent/.pi/ ← your Pi config, sessions, API keys (persisted)
Everything else on your Mac is invisible.
Convenience: shell alias
Add this to ~/.zshrc so you can just type pi from any project:
alias pi='~/pi-docker/run.sh'
Then reload: source ~/.zshrc
Now:
cd ~/projects/my-app
pi
Referencing files from another project
By default Pi can only see the current project. To give it access to another project, edit run.sh and uncomment the EXTRA_MOUNTS block:
# run.sh — EXTRA_MOUNTS section
EXTRA_MOUNTS+=("-v" "$HOME/projects/shared-lib:/workspace/shared-lib:ro")
- The path before
:is the folder on your Mac. - The path after
:is where it appears inside the container. :romeans read-only — Pi can reference it but cannot modify it.
Multiple mounts:
EXTRA_MOUNTS+=("-v" "$HOME/projects/shared-lib:/workspace/shared-lib:ro")
EXTRA_MOUNTS+=("-v" "$HOME/projects/design-tokens:/workspace/design-tokens:ro")
Inside Pi, reference those files normally:
"check /workspace/shared-lib/src/utils.ts for the helper function"
Per-project run scripts (recommended)
If each project needs different mounts, copy run.sh into the project root
and customize the EXTRA_MOUNTS for that project:
cp ~/pi-docker/run.sh ~/projects/my-app/pi.sh
chmod +x ~/projects/my-app/pi.sh
# edit my-app/pi.sh → add its specific extra mounts
Then run Pi from that project with:
cd ~/projects/my-app
./pi.sh
Pi config & sessions
Pi’s global config (~/.pi/) is mounted from your Mac into every container.
This means:
- Your API keys set via
/loginor/settingspersist. - Sessions and history are saved across runs.
- Installed Pi packages and extensions carry over.
To set a default provider/model for all projects, create or edit
~/.pi/agent/settings.json on your Mac:
{
"defaultProvider": "anthropic",
"defaultModel": "claude-sonnet-4-20250514"
}
To override settings for one project only, create .pi/settings.json
inside that project:
{
"defaultProvider": "openai",
"defaultModel": "gpt-4o"
}
Updating Pi
Since Pi is baked into the Docker image, update it by rebuilding:
~/pi-docker/build.sh
This pulls the latest @earendil-works/pi-coding-agent from npm.
Security summary
| Protection | How |
|---|---|
| No host root access | Container runs as non-root agent user |
| No kernel privilege escalation | --cap-drop=ALL + --security-opt no-new-privileges |
| Filesystem isolation | Only explicitly mounted paths are visible |
| No leftover containers | --rm removes the container on exit |
| Per-project isolation | Each docker run is a fresh container |
Troubleshooting
”Docker is not running” → Open Docker Desktop from Applications.
”Image not found”
→ Run ~/pi-docker/build.sh first.
Pi can’t find my API key
→ Make sure ANTHROPIC_API_KEY (or equivalent) is exported in ~/.zshrc
and you’ve run source ~/.zshrc in the current terminal.
Pi can’t see a file from another project
→ Add a mount for it in the EXTRA_MOUNTS section of run.sh.
Paths must be absolute (use $HOME/... not ~/...).
Changes to files aren’t visible on my Mac → The mount is two-way by default. If Pi wrote a file, it’s already on your Mac at the same relative path inside your project folder.