pi cf5c60a342
Publish Docker Image / resolve-versions (push) Successful in 6s
Publish Docker Image / base-decide (push) Successful in 14s
Publish Docker Image / build-base (push) Successful in 33m49s
Publish Docker Image / smoke (push) Successful in 5m23s
Publish Docker Image / build-variant (push) Successful in 17m58s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / promote-base-latest (push) Successful in 10s
fix(base): work around mempalace diary_write top-level anyOf
Anthropic's tools API rejects top-level anyOf/oneOf/allOf in
input_schema. Mempalace 3.3.x/3.4.0 advertise diary_write with
`anyOf: [{required:[entry]}, {required:[content]}]`, breaking pi
on first prompt with:

  tools.<n>.custom.input_schema: input_schema does not support
  oneOf, allOf, or anyOf at the top level

Patch the installed mcp_server.py after `uv tool install` to drop
the anyOf block and require ["agent_name", "entry"] instead. The
handler still accepts `content` server-side as a kwarg alias, so
callers using either name keep working.

The workaround is idempotent and self-deactivating: once upstream
ships the fix the regex no longer matches and the RUN is a silent
no-op.

Also pin mempalace to 3.4.0 via a new MEMPALACE_VERSION build arg
so future bumps are a deliberate, reviewable diff rather than an
implicit pull of latest (an implicit upgrade is what swept the
broken schema in unannounced).

Refs MemPalace/mempalace#1728, MemPalace/mempalace#1735
2026-06-10 16:25:26 +02:00
2026-05-14 19:57:17 +02:00

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 template
  • pi-extensions — TypeScript extensions for pi (preview, MCP bridges, mempalace integration, etc.)
  • pi-fork — the fork tool for spawning sub-agents
  • pi-observational-memory — the recall tool 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. converter
  • graphvizdot rendering for diagram pipelines
  • imagemagick — image conversion / resizing (invoked as magick)

Language toolchains

  • python3 + python3-venv + python3-pip (system Python)
  • uv + uvx — fast Python package manager (preferred over pip/venv)
  • nodejs (v22) + npm
  • gcc, g++, make — C/C++ build tools
  • rustup-init — Rust toolchain installer (toolchains opt-in at runtime)
  • Optional INSTALL_GO=true build 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 API
  • age, 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 adds texlive-xetex for 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:

  1. Configure SSO on the host: aws configure sso
  2. Bind-mount ~/.aws:/home/developer/.aws:ro
  3. Set AWS_PROFILE and AWS_REGION in .env
  4. Inside the container: aws sso login if 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:

  1. Base (Dockerfile.base) — produces joakimp/pi-devbox:base-<hash> where <hash> is content-addressed over Dockerfile.base, rootfs/, and entrypoint*.sh. Rebuilt only when these change.
  2. Variant (Dockerfile.variant) — FROM ${BASE_IMAGE} and adds the pi install. The :latest and vX.Y.Z tags 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.0 is 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

S
Description
pi coding-agent container built on opencode-devbox base
Readme 552 KiB
Languages
Shell 100%