Self-contained build chain — own Dockerfile.base + Dockerfile.variant
+ entrypoint scripts + rootfs + CI pipeline. Previously v0.79.0 and
earlier were thin re-brands of opencode-devbox's pi-only variant
(joakimp/pi-devbox:base-pi-only built by opencode-devbox CI).
Architectural changes:
- Replace 5-line Dockerfile shim with full base+variant pair.
- Adapt CI workflow from opencode-devbox/docker-publish-split.yml,
simplified to a single variant. Includes content-addressed base hash,
PI_VERSION concrete-resolution to defeat registry-buildcache footgun,
crane-based base-latest promotion, and the c6f9d11 smoke-test gate.
- pi-devbox releases no longer require rebuilding opencode-devbox first.
Base image additions:
- pandoc, graphviz, imagemagick, yq — broadly useful, ~260 MB total.
- tldr (tealdeer) — Rust port replaces Node tldr global, saves 135 MB.
- /etc/tmux.conf with base-index 0 + pane-base-index 0 — required for
the planned :latest-studio variant; pi-studio hard-codes :0.0 target.
Smoke test:
- New checks for pandoc, graphviz, imagemagick, yq, tldr, tmux config,
/tmp/sshcm directory.
- Image-size measurement now sums docker history layers (the prior
inspect --format='{{.Size}}' returned only the variant-unique layer
with the new base/variant split, understating by 2+ GB).
- Threshold 2850 → 3500 MB to absorb base additions + arch margin.
Image size:
- Local arm64 build: 3.20 GB. ~390 MB up from prior pi-only equivalent.
- Will tighten threshold once amd64 actuals settle in CI.
Pre-1.0 history preserved at tag pre-v1.0.0-decouple-backup.
Future work:
- v1.1.0: :latest-studio variant (adds pi-studio).
- v1.2.0: :latest-studio-tex variant (adds texlive-xetex for PDF).
- opencode-devbox v2.0.0 will retire INSTALL_PI / pi-only paths.
pi-devbox
A self-contained Docker image for running pi — the pi coding-agent — in an isolated, reproducible Linux environment with a curated set of developer tooling, AI memory, and shell improvements.
pi-devbox is opinionated about what's inside but unopinionated about how
you use it: a single docker compose up gives you an interactive
container with pi, a stack of modern CLI tools, MemPalace for persistent
agent memory across sessions, and a UID-aligned /workspace mount so
files you edit inside the container appear with your normal ownership
on the host.
What's inside
The pi coding-agent
pi— the pi-coding-agent CLI (@earendil-works/pi-coding-agent)pi-toolkit— keybindings, AWS env loader, settings templatepi-extensions— TypeScript extensions for pi (preview, MCP bridges, mempalace integration, etc.)pi-fork— theforktool for spawning sub-agentspi-observational-memory— therecalltool for session compaction
MemPalace (AI memory)
mempalace— local-first agent memory system (29 MCP tools)mempalace-toolkit— bash wrappers for session/docs mining- ChromaDB embedding model pre-warmed at build time (
all-MiniLM-L6-v2)
The host-mounted palace at ~/.mempalace is shared across the host and
this container so all your agents share one brain.
Modern CLI tooling
| Tool | Purpose |
|---|---|
nvim |
Neovim text editor |
tmux |
Terminal multiplexer (configured for 0-indexed sessions) |
ripgrep, fd |
Fast file content / filename search |
fzf |
Fuzzy finder |
bat |
Syntax-highlighted cat |
eza |
Modern ls |
zoxide |
Smart cd |
jq, yq |
JSON / YAML query and transformation |
tldr (tealdeer) |
Quick command examples |
git, git-lfs, git-crypt |
Git + extensions |
gitleaks |
Secret scanning pre-commit hook |
gosu |
Privilege de-escalation in entrypoint |
htop, tree, less |
Inspection utilities |
Document and image tooling
pandoc— universal Markdown↔HTML/Org/RST/etc. convertergraphviz—dotrendering for diagram pipelinesimagemagick— image conversion / resizing (invoked asmagick)
Language toolchains
python3+python3-venv+python3-pip(system Python)uv+uvx— fast Python package manager (preferred over pip/venv)nodejs(v22) +npmgcc,g++,make— C/C++ build toolsrustup-init— Rust toolchain installer (toolchains opt-in at runtime)- Optional
INSTALL_GO=truebuild arg for Go
For Python REPLs and notebooks beyond the system interpreter, see the uv-driven REPL recipes section.
Cloud + secrets
- AWS CLI v2 — for SSO + Bedrock auth
gitea-mcp— MCP server for Gitea APIage,git-crypt— encryption tooling
SSH and networking
- OpenSSH client with ControlMaster auto preconfigured on a
writable socket path (
/tmp/sshcm/). Mitigates ssh banner-exchange failures behind CGNAT-restricted residential ISPs (~4-flow caps) by multiplexing many ssh calls over one TCP flow. - A LAN-access helper that auto-configures ssh jump-via-host on VM-backed hosts (OrbStack / Docker Desktop on macOS) so the container can reach the host's directly-attached LAN peers.
Quickstart
Prerequisites
- Docker or OrbStack (recommended on macOS)
- Optional: AWS credentials configured on the host if you'll use the Bedrock LLM provider
Pull and run
git clone https://gitea.jordbo.se/joakimp/pi-devbox
cd pi-devbox
cp .env.example .env # edit if needed
docker compose up -d
docker compose exec devbox bash
You're now in the container as user developer with pi on PATH and
your host workspace mounted at /workspace.
To start pi:
pi
First-run pi-toolkit and pi-extensions install steps run automatically
on container start; symlinks are written to ~/.pi/agent/ on the
named volume (so they persist across container recreations).
Stop / recreate / update
docker compose down # stop, keep volumes
docker compose down -v # stop, wipe per-container volumes (palace data is bind-mounted, so unaffected)
docker compose pull # fetch latest image
docker compose up -d --force-recreate
Image variants
Currently published:
| Tag | Includes | Size (approx.) |
|---|---|---|
joakimp/pi-devbox:latest |
base + pi + tooling | ~3.2 GB |
joakimp/pi-devbox:vX.Y.Z |
pinned-version equivalent | ~3.2 GB |
Planned for upcoming minor releases:
joakimp/pi-devbox:latest-studio— adds pi-studio for browser-based prompt editing, KaTeX/Mermaid preview, and literate REPLs (Shell / Python / IPython / Julia / R / GHCi / Clojure). Adds ~50 MB.joakimp/pi-devbox:latest-studio-tex— also addstexlive-xetexfor PDF export from Studio. Adds ~600 MB on top of-studio.
docker-compose.yml — basic shape
name: pi-devbox
services:
devbox:
image: joakimp/pi-devbox:latest
container_name: pi-devbox
stdin_open: true
tty: true
env_file: .env
environment:
- TZ=${TZ:-Europe/Stockholm}
- TERM=xterm-256color
- AWS_PROFILE=${AWS_PROFILE:-}
- AWS_REGION=${AWS_REGION:-eu-west-1}
volumes:
# Workspace: your host source tree, read-write
- ${HOST_WORKSPACE:-./workspace}:/workspace:rw
# SSH keys: read-only from host
- ${HOME}/.ssh:/home/developer/.ssh:ro
# AWS config: read-only from host
- ${HOME}/.aws:/home/developer/.aws:ro
# MemPalace: bind-mounted so host pi and container pi share a brain
- ${HOME}/.mempalace:/home/developer/.mempalace:rw
# Per-container persistent state
- devbox-pi-config:/home/developer/.pi
- devbox-bash-history:/home/developer/.cache/bash
- devbox-nvim-data:/home/developer/.local/share/nvim
- devbox-uv-tools:/opt/uv-tools
- devbox-chroma-cache:/home/developer/.cache/chroma
volumes:
devbox-pi-config:
devbox-bash-history:
devbox-nvim-data:
devbox-uv-tools:
devbox-chroma-cache:
See .env.example in the repo for available environment variables.
uv-driven REPL recipes
uv is installed in the base image and is the recommended way to run Python interpreters and notebooks without bloating the image:
| Goal | One-liner |
|---|---|
| IPython REPL | uv run --with ipython ipython |
| IPython + scientific stack | uv run --with ipython --with numpy --with matplotlib --with pandas ipython |
| JupyterLab (browser, port-forward needed) | uv run --with jupyterlab jupyter lab --no-browser --port 8888 |
| Marimo (modern alternative) | uv run --with marimo marimo edit --port 8889 |
For long-lived environments, prefer a project venv:
cd /workspace/myproj
uv init && uv add ipython numpy matplotlib
# then:
uv run ipython
pyproject.toml + uv.lock then capture the dependency state and
travel with the project in git.
uv only manages Python. For other languages:
| Toolchain | How to add |
|---|---|
| R | sudo apt-get install r-base-core (~200 MB) |
| GHCi (Haskell) | sudo apt-get install ghc (~700 MB) |
| Clojure | sudo apt-get install clojure (~150 MB + JVM) |
| Julia | juliaup is planned for an upcoming release |
These are runtime opt-ins and persist only in the container's writable
layer — they don't survive docker compose down -v or image updates.
tldr — first-run cache
The tldr command (provided by tealdeer) shows a "Page cache not
found" message on first invocation. To populate the cache:
tldr --update
This fetches ~1500 command pages from the tldr-pages
project and caches them in ~/.cache/tealdeer/. After that, tldr ls,
tldr docker, etc. work instantly. Re-run tldr --update periodically
to refresh.
Volumes and persistence
| Path inside container | Volume | What survives |
|---|---|---|
/workspace |
host bind-mount | host filesystem |
~/.ssh |
host bind-mount (read-only) | host filesystem |
~/.aws |
host bind-mount (read-only) | host filesystem |
~/.mempalace |
host bind-mount | host filesystem |
~/.pi |
named volume devbox-pi-config |
down -v wipes |
~/.cache/bash |
named volume | down -v wipes |
~/.local/share/nvim |
named volume | down -v wipes |
/opt/uv-tools |
named volume | down -v wipes |
~/.cache/chroma |
named volume | down -v wipes |
Anything not on a volume is on the writable layer and is lost on container recreate.
MemPalace integration
MemPalace is installed in the base image and pre-warmed with the ChromaDB ONNX embedding model so first-time semantic search is instant.
The palace data lives at ~/.mempalace/palace on the host
(bind-mounted into the container). This means:
- A pi running on the host and a pi running inside this container see the same palace.
- SQLite's WAL mode handles concurrent reads + single writer cleanly, so simultaneous use is safe in practice.
mempalace-session and mempalace-docs are on PATH for one-off
session/docs mining; the 29 MCP tools (search, kg-query, drawer-add,
diary-write, etc.) are wired into pi automatically by the pi-extensions
mempalace bridge.
SSH and ControlMaster
The base image preconfigures Host * ssh defaults:
ControlMaster auto
ControlPath /tmp/sshcm/%r@%h:%p
ControlPersist 10m
The socket directory /tmp/sshcm/ is created mode 700 on every
container start (per-container, tmpfs-friendly). Multiple ssh calls
to the same host within 10 minutes reuse the master TCP flow —
important on residential ISPs with CGNAT per-destination flow caps
(~4 flows on most European broadband; symptoms are
kex_exchange_identification: Connection closed by remote host on
the 5th+ concurrent ssh).
User-level overrides in ~/.ssh/config win because Debian's
/etc/ssh/ssh_config includes /etc/ssh/ssh_config.d/*.conf before
the Host * block.
tmux and 0-indexed sessions
The image installs /etc/tmux.conf with:
set -g base-index 0
set -g pane-base-index 0
This is the default tmux indexing. It's baked here because pi-studio
(planned for :latest-studio) hard-codes its tmux send target to
<session>:0.0. If you override base-index to 1 in a personal
~/.tmux.conf, pi-studio will fail with "can't find window: 0".
AWS Bedrock auth
If you use Bedrock as pi's LLM provider:
- Configure SSO on the host:
aws configure sso - Bind-mount
~/.aws:/home/developer/.aws:ro - Set
AWS_PROFILEandAWS_REGIONin.env - Inside the container:
aws sso loginif needed; pi picks up the profile via the env vars.
The pi-toolkit AWS env loader (in ~/.pi/agent/) prepares Bedrock
inference-profile model IDs (with eu. / us. prefixes) automatically.
Build pipeline
pi-devbox is built from this repo's CI in two phases:
- Base (
Dockerfile.base) — producesjoakimp/pi-devbox:base-<hash>where<hash>is content-addressed overDockerfile.base,rootfs/, andentrypoint*.sh. Rebuilt only when these change. - Variant (
Dockerfile.variant) —FROM ${BASE_IMAGE}and adds the pi install. The:latestandvX.Y.Ztags are produced from this layer; future Studio variants will extend further.
Tag naming:
| Tag | Stage |
|---|---|
base-<hash> |
base image — internal building block |
base-latest |
promoted alias of the most recent base |
latest, vX.Y.Z |
variant: base + pi |
CI resolves PI_VERSION to a concrete version string before building
to defeat a registry-buildcache hit on npm install -g pi-coding-agent@latest (the build-arg string would otherwise be
byte-identical across releases and the layer would silently reuse the
previous version's bytes).
Troubleshooting
Image grew unexpectedly
docker history joakimp/pi-devbox:latest shows per-layer sizes. The
biggest layers are typically the apt block (~600 MB), pi npm install
(~330 MB), MemPalace + ChromaDB (~315 MB), AWS CLI (~270 MB), Node.js
(~200 MB).
pi can't reach LAN peers on macOS
The LAN-access helper (/usr/local/lib/pi-devbox/setup-lan-access.sh)
auto-runs on container start and writes ~/.ssh-local/config with a
ssh-jump-via-host configuration. Set DEVBOX_LAN_ACCESS=jump and
HOST_SSH_USER=<your-mac-user> in .env if auto-detection fails.
Smoke-testing a local build
./scripts/smoke-test.sh joakimp/pi-devbox:latest
Versioning and release
pi-devbox follows semver-ish:
- Major — architectural changes.
v1.0.0is the first decoupled release (independent of opencode-devbox). - Minor — new variants, significant base additions.
- Patch — pi version bumps, smaller fixes.
The pi --version inside the image is asserted by smoke tests to
match the release tag's pi component, so version drift between the
image and the tag is caught at CI time.
Acknowledgements
pi-devbox was originally a thin re-brand of the pi-only variant of
opencode-devbox.
It was decoupled at v1.0.0 so it could evolve at its own pace, with
self-contained docs and a focused, pi-centric image. Significant base
infrastructure (the SSH ControlMaster setup, MemPalace integration,
the entrypoint UID/GID dance) was adopted from there.
The pi coding-agent itself is @earendil-works/pi-coding-agent.
License
MIT