Files
pi-devbox/DOCKER_HUB.md
T
Joakim Persson 12f99c49e3
Lint / skill-floor (push) Successful in 7s
Lint / hadolint (push) Successful in 15s
Lint / actionlint (push) Successful in 17s
Lint / doc-drift (push) Successful in 14s
Publish Docker Image / lint-gate (push) Successful in 21s
Publish Docker Image / resolve-versions (push) Successful in 10s
Publish Docker Image / base-decide (push) Successful in 9s
Publish Docker Image / build-base (push) Successful in 55m32s
Publish Docker Image / smoke (push) Failing after 8m50s
Publish Docker Image / build-variant (push) Has been skipped
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Publish Docker Image / smoke-studio (push) Failing after 9m55s
Publish Docker Image / build-variant-studio (push) Has been skipped
docs(v1.10.0): adopt pi's fullscreen default and document the way back
Decision: keep pi 1.0.0's default (tuiMode "fullscreen"). No tuiMode is baked,
so the image follows upstream rather than pinning the fleet to either mode -
but "we inherited a changed default" is only acceptable if the revert is
written down, so it now is.

README gains a "Terminal UI mode" section ahead of the pi-atelier section,
because the two interact. It gives all three scopes, taken from pi 1.0.0's own
docs/settings.md and docs/cli.md rather than from the changelog prose:

  - permanent: "tuiMode": "regular" in ~/.pi/agent/settings.json
  - one session: pi --tui-mode regular   (the flag is real: cli.md:221)
  - one project: the same key in .pi/settings.json, which overrides the agent
    directory

It also documents the four related settings (fullscreenExitOutput,
fullscreenScrollbar, fullscreenCopyOnSelect, fullscreenWheelScrollLines) and
two things specific to this image:

  - tmux: fullscreen uses the alternate screen, so tmux copy-mode shows the
    pane history AROUND pi, not pi's transcript. That is the concrete reason
    someone here would want "regular" back.
  - fullscreenWheelScrollLines "auto" behaves differently over SSH (caps fast
    wheel spins at 6 lines/event) than in a local macOS terminal (1 line) -
    worth naming because this container is normally driven over SSH.
  - pi-atelier works in BOTH modes (upstream has handled regular vs fullscreen
    renderers separately since pi 0.84), so reverting costs nothing. Stated so
    nobody assumes the sidebar is the price of the old scrollback.

The python3 merge snippet in that section was RUN before being documented:
against a realistic settings.json under an overridden HOME, twice, confirming
it is idempotent and preserves sibling keys including nested objects and the
_comment fields the seeded file uses. Documented code that has never been
executed is a guess.

DOCKER_HUB.md gets a short version of the same note, because CI reads it from
the TAG and POSTs it to Docker Hub as full_description - a behaviour change
this visible should not require reading the repo to undo.

Gates: doc-drift 23 OK / 0 DRIFT / 0 SKIP / 0 FAIL; base-hash, workflow-shell,
skill-floor, lint-shell all rc=0.
2026-10-02 09:00:51 +02:00

11 KiB

pi-devbox

A self-contained Docker container for the pi coding-agent — pi + companion repos + MemPalace + a curated set of dev tooling, ready to run.

Current :latest ships pi {{PI_VERSION}} (resolved at build time; see Versioning).

Image variants

Tag Architectures Size (compressed) What you get
joakimp/pi-devbox:latest amd64, arm64 ~1.23 GB Self-contained: base + pi {{PI_VERSION}} + companions
joakimp/pi-devbox:vX.Y.Z amd64, arm64 same Pinned semver release
joakimp/pi-devbox:latest-studio amd64, arm64 ~1.25 GB latest + pi-studio: browser prompt editor, KaTeX/Mermaid preview, tmux-backed literate REPLs
joakimp/pi-devbox:vX.Y.Z-studio amd64, arm64 same Pinned semver studio release
joakimp/pi-devbox:base-latest amd64, arm64 ~1.17 GB Base layer alias (internal building block; pull :latest instead)
joakimp/pi-devbox:base-<hash> amd64, arm64 ~1.17 GB Content-addressed base; immutable. Stable parent for variant rebuilds.

pi-studio (-studio tags): launch with /studio --no-browser --port 8765 inside a pi session. The server binds 127.0.0.1 inside the container, so reach it via host networking or a loopback bridge (and ssh -L for a remote host; mosh needs a parallel ssh -L). Full recipe: README → Using pi-studio.

Quick start

One-shot, no persistence:

docker run -it --rm \
  -v "$PWD":/workspace \
  -v "$HOME/.ssh":/home/developer/.ssh:ro \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  joakimp/pi-devbox:latest pi

For a fully-configured environment with persistent settings, MemPalace memory, neovim plugins, and shell history surviving container recreation, use docker-compose. You don't need to clone the repo — just grab two template files:

mkdir -p ~/pi-devbox && cd ~/pi-devbox
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 — set WORKSPACE_PATH, an LLM API key (ANTHROPIC_API_KEY,
# OPENAI_API_KEY, GEMINI_API_KEY, or AWS_*), and your git identity.
docker compose run --rm devbox pi

Full setup guide — authentication for each provider (Anthropic, OpenAI, Gemini, AWS Bedrock SSO + static), persistence model, configuration reference, build args, troubleshooting: https://gitea.jordbo.se/joakimp/pi-devbox#readme

What's inside

pi and companions

  • pi {{PI_VERSION}} (@earendil-works/pi-coding-agent) — installed at /usr/bin/pi, pinned to an audited version (not npm latest)
  • pi-atelier — TUI sidebar (ordered panels, split-pane, themes), vendored at /opt/pi-atelier and pinned to an audited tag; the exact tag is in the image labels (se.jordbo.pi-devbox.pi-atelier-version) and /etc/pi-devbox/build-manifest.json
  • pi-toolkit — keybindings (mosh/tmux-friendly Shift+Enter, Ctrl+J, Alt+J newline bindings), AWS env loader, settings template
  • pi-extensions — 7 user-facing extensions: ext-toggle, mcp-loader, todo, ssh-controlmaster, notify, git-checkpoint, confirm-destructive
  • fork (pi-fork) and recall (pi-observational-memory) tools
  • mempalace bridge — MCP extension auto-symlinked so pi reads/writes the host-mounted palace
  • image-baked agent skills — skills under /usr/local/share/pi-devbox/skills/ (e.g. pi-devbox-environment, which teaches agents the container's persistence/networking/DNS/tmux/REPL specifics) are symlinked into ~/.agents/skills/ on start, available with or without a mounted skillset repo

The entrypoint deploys/registers all of these on first container start. Re-running is idempotent and preserves user edits.

Terminal UI mode — fullscreen by default. pi 1.0.0 made the TUI fullscreen, and this image adopts upstream's default. Fullscreen uses the terminal's alternate screen, so the transcript no longer lands in your terminal's (or tmux's) native scrollback. To get the previous behaviour back, set "tuiMode": "regular" in ~/.pi/agent/settings.json, or pass pi --tui-mode regular for a single session. The bundled pi-atelier sidebar works in both modes. See the README for the related fullscreenExitOutput / fullscreenScrollbar / fullscreenCopyOnSelect / fullscreenWheelScrollLines settings — the last one behaves differently over SSH, which is how this container is usually driven.

MemPalace (persistent agent memory)

  • MemPalace + MCP server — semantic search over conversation history, knowledge graph, diary; queryable via 29 mempalace_* tools inside pi
  • ChromaDB ONNX embedding model pre-warmed at build time (all-MiniLM-L6-v2)
  • Bind-mount your host's ~/.mempalace and the host-pi and container-pi share one brain

Document and image tooling

  • pandoc — universal Markdown↔HTML/Org/RST/etc. conversion. Useful well beyond pi: agent-driven doc exports, format conversion, etc.
  • Typst — markup-based typesetting, used as pandoc's --pdf-engine
  • graphviz (dot) — diagram rendering pipelines
  • imagemagick (magick) — image conversion / resizing

Browser automation

  • agent-browser — CLI for driving a real browser (open pages, click/fill/eval, snapshot the DOM, screenshots) so agents can verify front-end work instead of guessing
  • Playwright + a headless Chromium are pre-installed and pinned together; AGENT_BROWSER_EXECUTABLE_PATH is preset to the baked browser, so agent-browser open <url> works out of the box with no setup
  • socat — TCP bridge used to expose the pi-studio server outside the container's loopback

Modern CLI tooling

  • Editor: neovim (system-wide termguicolors default; bring your own config/plugins), tmux (configured for 0-indexed sessions)
  • Search/nav: ripgrep, fd, fzf, zoxide
  • Display: bat, eza, htop, tree
  • Data: jq, yq, sqlite3, bc/dc, column
  • Help: tldr (tealdeer — Rust port; run tldr --update once to populate cache)
  • Git: git-lfs, git-crypt, gitleaks (for pre-commit secret scanning)
  • Build: gcc, g++, make, patch
  • Misc: gosu, age, rsync, less

Language toolchains

  • Python: system Python 3 + uv (preferred) for fast Python package management. Run any Python REPL/notebook stack on demand without bloating the image:
    uv run --with ipython ipython
    uv run --with jupyterlab jupyter lab --no-browser --port 8888
    uv run --with marimo marimo edit
    
  • Node.js v24 LTS + npm (used by pi itself)
  • Rust — rustup-init is on PATH; install toolchains on demand
  • Go — opt-in via --build-arg INSTALL_GO=true if rebuilding from source

Cloud + secrets

  • AWS CLI v2 — for SSO + Bedrock auth (pi's preferred LLM provider for the maintainer's setup)
  • Gitea MCP server — for Gitea API access from inside pi
  • 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). A read-only ~/.ssh carrying a per-host ControlPath (common CGNAT configs) is handled too — redirected to a writable socket dir for both pi --ssh and dssh/dscp.
  • 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 (dssh <peer> alias; DEVBOX_LAN_ACCESS / HOST_SSH_USER).

Versioning

From v1.0.0 onward, pi-devbox uses semver:

  • Major — architectural changes. v1.0.0 is the first decoupled release, where pi-devbox got its own self-contained build chain (previously it was a thin re-brand of opencode-devbox's pi-only variant).
  • Minor — new image variants, significant base additions.
  • Patch — pi version bumps, smaller fixes.

The pi binary version inside any given release is shown in this description (currently {{PI_VERSION}} for :latest) and asserted by smoke tests to match what's documented — version drift is caught at CI time, not on user pull.

Pre-v1.0.0 history. Tags v0.74.0…v0.79.0 followed the pi npm version directly (v{pi_version}[letter]). Those images remain on Hub but are deprecated in favor of :latest / :v1.X.Y. The legacy :base-pi-only* tags were CI artifacts of the old opencode-devbox-based build pipeline; they will be removed in a future opencode-devbox v2.0.0.

Build pipeline

pi-devbox is built in two phases:

  1. Base (Dockerfile.base) → base-<hash> tag, content-addressed over Dockerfile.base + rootfs/ + entrypoint*.sh. Rebuilt only when those change.
  2. Variant (Dockerfile.variant) → :latest and :vX.Y.Z. FROMs the base, adds the pi install + companions.

base-latest is an alias of the most recent base.

Persistent state

User edits and pi-installed packages survive container recreation when you mount these named volumes. Use the included docker-compose.yml and they're set up automatically.

Volume Mount point What it holds
devbox-pi-config /home/developer/.pi/ pi settings, extension toggles, sessions, user-installed pi packages (npm install -g, pi install npm:…)
devbox-shell-history /home/developer/.cache/bash bash history
devbox-zoxide /home/developer/.local/share/zoxide zoxide directory jump database
devbox-nvim-data /home/developer/.local/share/nvim neovim plugin & Mason package state
devbox-uv /home/developer/.local/share/uv uv Python installs and tool cache
devbox-ssh-local /home/developer/.ssh-local LAN-jump key (one-time host authorization survives recreate)

Optional volumes for MemPalace (commented out by default — uncomment in docker-compose.yml to persist conversation memory across restarts):

Volume Mount point What it holds
devbox-palace /home/developer/.mempalace palace data (drawers, knowledge graph, embeddings)
devbox-chroma-cache /home/developer/.cache/chroma ChromaDB embedding model cache (~80 MB, can be rebuilt)

User-installed pi packages

NPM_CONFIG_PREFIX is set inside the container to /home/developer/.pi/npm-global. Anything you pi install npm:<pkg> or npm install -g lands on the devbox-pi-config named volume — survives container recreation and image rebuilds. A user-installed pi wins over the baked one via PATH order, so you can pin a different pi version without rebuilding the image.

Source

License

MIT (the image; pi and the bundled tools each carry their own licenses). See LICENSE and THIRD_PARTY.md in the source repo.