pi-devbox
A Docker container with pi coding-agent pre-installed, built on the opencode-devbox base image. Persistent state, full dev toolchain, MemPalace memory, and provider-agnostic LLM auth — in one docker compose run.
Hub:
joakimp/pi-devbox· multi-arch (amd64 + arm64) Source: gitea.jordbo.se/joakimp/pi-devbox
What's inside
pi-devbox is a thin re-brand of the pi-only build — it FROMs
joakimp/pi-devbox:base-pi-only and adds no layers of its own. That base build
is produced by opencode-devbox's CI (from opencode-devbox/Dockerfile.variant
with INSTALL_OPENCODE=false, the single source of truth for the pi install +
companions) but is published into this repo as the internal building-block
tag base-pi-only — not under opencode-devbox, so an "opencode-devbox" tag
never ships without opencode. Everything below is inherited from that build,
which is lean and pi-focused — no opencode.
Base tooling:
- Debian trixie (current stable)
- Node.js (LTS), uv (Python), rustup (Rust on-demand)
- AWS CLI v2 (with Bedrock support)
- MemPalace + MCP server — persistent agent memory across sessions; queryable via
mempalace_*tools inside pi - Gitea MCP server
- Dev tools: neovim (LazyVim), tmux, bat, eza, fzf, zoxide, ripgrep, jq, git-lfs, make
- Shell: bash with history tuning, prefix-search, fzf/zoxide integration
- Host-OS-agnostic LAN access — on VM-backed hosts (macOS OrbStack / Docker Desktop) the entrypoint sets up the host as an SSH jump so you can reach LAN peers (
dsshalias;DEVBOX_LAN_ACCESS/HOST_SSH_USERenv). No-op on native Linux.
pi and companions:
- pi (
@earendil-works/pi-coding-agent) — baked at/usr/bin/pi, version pinned by the pi-only base build - pi-toolkit — mosh/tmux-friendly keybindings (Shift+Enter, Ctrl+J, Alt+J newline), AWS env loader, settings template
- pi-extensions — 7 extensions:
ext-toggle,mcp-loader,todo,ssh-controlmaster,notify,git-checkpoint,confirm-destructive forktool (pi-fork) andrecalltool (pi-observational-memory) — baked into/optand registered at runtime- mempalace bridge — auto-symlinked MCP extension so pi reads/writes the same palace as opencode-devbox's palace
(opencode itself is not included — that's the difference from opencode-devbox:latest-with-pi. If you want both opencode and pi in one image, use that variant instead.)
The entrypoint deploys/registers all of these on first container start. Idempotent and preserves user edits.
Quick start (no git clone)
If you just want to run pi-devbox and don't plan to modify the source, grab the two template files and go:
mkdir -p ~/pi-devbox && cd ~/pi-devbox
# Pull the docker-compose.yml and .env template
curl -O https://gitea.jordbo.se/joakimp/pi-devbox/raw/branch/main/docker-compose.yml
curl -fsSL https://gitea.jordbo.se/joakimp/pi-devbox/raw/branch/main/.env.example -o .env
# Edit .env — at minimum set WORKSPACE_PATH, an LLM API key, and your git identity
$EDITOR .env
# Pull and run pi
docker compose run --rm devbox pi
docker compose run --rm devbox (no command) drops you into bash; you can then run pi, aws sso login, etc. manually.
To attach a second terminal to the same container (e.g. shell while pi is running):
docker compose exec -u developer devbox bash
Quick start (with git clone)
If you want to follow upstream changes, run a customized fork, or rebuild the image yourself:
git clone https://gitea.jordbo.se/joakimp/pi-devbox
cd pi-devbox
cp .env.example .env
$EDITOR .env
docker compose run --rm devbox pi
Authentication
pi reads provider credentials from environment variables, which the container picks up from .env automatically.
Anthropic (Claude)
ANTHROPIC_API_KEY=sk-ant-...
Generate a key at https://console.anthropic.com/settings/keys.
OpenAI
OPENAI_API_KEY=sk-...
Google Gemini
GEMINI_API_KEY=...
AWS Bedrock (e.g. Claude on Bedrock)
Two paths — pick one:
A) Static credentials (simplest, lower-trust environments only):
AWS_REGION=eu-west-1
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
B) AWS SSO (recommended for corporate AWS, requires mounting ~/.aws):
AWS_REGION=eu-west-1
AWS_PROFILE=your-profile
Then in your docker-compose.yml, uncomment the ~/.aws bind-mount:
volumes:
- ~/.aws:/home/developer/.aws
Inside the container, run aws sso login once per session. The token cache lives on the bind-mount, so subsequent pi invocations pick it up automatically. The pi-toolkit's pi-env.zsh (deployed to ~/.config/pi/) auto-sources AWS_PROFILE/AWS_REGION whenever a shell starts.
First-run pi configuration
On first start, pi reads ~/.pi/agent/settings.json (auto-bootstrapped from the pi-toolkit template). Edit it inside the container to pick a default provider/model:
docker compose exec -u developer devbox bash
$EDITOR ~/.pi/agent/settings.json
The file is rewritten by pi at runtime (e.g. lastChangelogVersion), so it lives on the devbox-pi-config named volume — your edits persist across container recreation.
For pi's full configuration model (provider list, model overrides, MCP integration, themes, extensions): https://github.com/earendil-works/pi#configuration.
Persistent state
Persistent state is what makes the difference between "use this once" and "make it my long-term coding environment". Everything important survives docker compose down and image upgrades; only docker compose down -v wipes the volumes.
| Volume | Mount point | What survives | Notes |
|---|---|---|---|
devbox-pi-config |
/home/developer/.pi/ |
pi settings.json, extension toggles, sessions, user-installed pi packages | NPM_CONFIG_PREFIX set inside the container so pi install npm:… and npm install -g lands here automatically |
devbox-shell-history |
/home/developer/.cache/bash |
bash history | Across container recreate |
devbox-zoxide |
/home/developer/.local/share/zoxide |
zoxide directory jump history | The z/zi shortcuts remember where you've been |
devbox-nvim-data |
/home/developer/.local/share/nvim |
neovim plugin & Mason package state | LazyVim plugins persist |
devbox-uv |
/home/developer/.local/share/uv |
uv-managed Python installs and tool cache | uv tool install results live here |
Optional persistent volumes
These are commented out in docker-compose.yml by default. Uncomment them if you want the corresponding state to persist:
| Volume | Mount point | What survives |
|---|---|---|
devbox-palace |
/home/developer/.mempalace |
MemPalace data — drawers, knowledge graph, embeddings. Treat as primary storage if you rely on agent memory. |
devbox-chroma-cache |
/home/developer/.cache/chroma |
ChromaDB embedding model cache (~80 MB; disposable, re-downloads in seconds) |
Workspace bind mount
/workspace is bind-mounted from WORKSPACE_PATH on the host (default ~/projects). Source code never lives inside the container — your editor on the host and pi inside the container see the same files.
SSH keys (read-only)
~/.ssh is mounted read-only at /home/developer/.ssh for git push/pull. The container does not write to it.
Configuration reference
All config flows through .env. The full list (with annotations) is in .env.example. Here's the most relevant subset:
| Variable | Default | Purpose |
|---|---|---|
WORKSPACE_PATH |
~/projects |
Host path mounted as /workspace |
SSH_KEY_PATH |
~/.ssh |
Host path for SSH keys (read-only) |
GIT_USER_NAME |
(empty) | Sets git config --global user.name inside container |
GIT_USER_EMAIL |
(empty) | Sets git config --global user.email inside container |
ANTHROPIC_API_KEY |
(unset) | Anthropic provider auth |
OPENAI_API_KEY |
(unset) | OpenAI provider auth |
GEMINI_API_KEY |
(unset) | Google Gemini auth |
AWS_PROFILE / AWS_REGION |
(unset) | AWS Bedrock SSO flow |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY |
(unset) | AWS Bedrock static creds |
GITEA_ACCESS_TOKEN / GITEA_HOST |
(unset) | Gitea MCP server (optional) |
GITHUB_PERSONAL_ACCESS_TOKEN |
(unset) | GitHub MCP server / git ops over HTTPS |
DEVBOX_LAN_ACCESS |
auto |
LAN-access mode: auto (jump only on VM-backed hosts), jump, off |
HOST_SSH_USER |
(unset) | Host username for the LAN SSH jump (see opencode-devbox README) |
LANG / LANGUAGE / LC_ALL |
en_US.UTF-8 |
Locale override |
Versioning
Tags follow the pi npm package version: v0.74.0, v0.75.0, … latest always points at the most recent successful release.
Container-level rebuilds on the same pi version (security updates, base bumps, fixes) get a letter suffix: v0.74.0b, v0.74.0c, …
The pi binary is inherited from joakimp/pi-devbox:base-pi-only, so a release of this image must be preceded by an opencode-devbox release that bakes the target pi version into base-pi-only. The smoke test enforces this (it asserts pi --version matches the tag).
Building from source
This image is a thin re-brand of the pi-only variant, so building it just pulls the base. To pin a specific pi-only build or hack on it:
git clone https://gitea.jordbo.se/joakimp/pi-devbox
cd pi-devbox
# Default tracks base-pi-only; override BASE_IMAGE to pin a build:
docker compose build \
--build-arg BASE_IMAGE=joakimp/pi-devbox:base-pi-only-v1.15.13c
docker compose up -d
To change the pi version, the pi extensions, or the install logic, edit
opencode-devbox/Dockerfile.variant (the single source of truth) and release
opencode-devbox — not this repo.
Build args supported:
| Arg | Default | Effect |
|---|---|---|
BASE_IMAGE |
joakimp/pi-devbox:base-pi-only |
Parent image (internal building-block tag) — set to a :base-pi-only-vX.Y.Z tag or a digest for reproducible builds |
Troubleshooting
pi --version works but pi exits immediately. First-run config probably hasn't been done. docker compose exec -u developer devbox bash, edit ~/.pi/agent/settings.json, ensure a provider is set and the matching API key is exported.
AWS SSO token expired. aws sso login from inside the container. The token cache is on the ~/.aws bind-mount, so it persists; expiration is the issue.
Anthropic 401 / OpenAI 401. Check the .env value made it in: docker compose exec devbox env | grep ANTHROPIC (etc).
pi prompts for an extension/MCP server you don't recognize. Either toggle it off via /ext inside pi, or rename the file: mv ~/.pi/agent/extensions/<name>.ts{,.off}.
Container won't start, error about /workspace. WORKSPACE_PATH in .env doesn't exist on the host. Create the directory or fix the path.
Pi-toolkit symlinks lost after docker compose down -v. That's expected — -v wipes named volumes. Don't do it unless you mean it. Container recreation without -v (the default) preserves all state.
Related
- opencode-devbox — the base image. Use this if you want both opencode and pi (it has a
latest-with-pivariant) or just opencode. - pi-toolkit — keybindings, env loader, settings template. Cloned into
/opt/pi-toolkitat image build time andinstall.shruns on container start. - pi-extensions — extension source. Same install pattern.
- mempalace-toolkit — MemPalace bring-up. The
mempalace.tsextension symlinked into~/.pi/agent/extensions/comes from here. - pi (upstream) — the coding-agent itself.
License
MIT (this image and its source). Pi and the bundled tools each carry their own licenses.