Skip to content

The image

Owner: pi-devbox (read at 47b4506, release v1.13.1)

pi-devbox is a Docker image that runs pi in an isolated Linux environment. Every machine in the fleet runs it, so every agent works in the same environment with the same tools and the same guardrails.

Why a container

  • One environment everywhere. A Mac, a Linux workstation and a cloud VM all run the same image. Something that works on one works on the others.
  • Rebuilt, not repaired. An update is a new image. The container is replaced instead of patched in place, and everything that matters lives outside it, on volumes and mounted directories.
  • A boundary for the agent. pi's own security guide calls running it inside a container usually the strongest option. Host files that are not mounted into the container are out of the agent's reach. See Safety guardrails for what is checked inside it.

What is inside

Group Contents
The agent pi, plus the third-party packages pi-fork, pi-observational-memory and pi-atelier
Our components pi-toolkit, pi-extensions and mempalace-toolkit, each checked out at a recorded commit
Memory the MemPalace client, with its embedding model downloaded at build time so the first search does not wait
Skills the baked skills and the global agent instructions (see Skills and tools)
Developer tools editors, tmux, fast search, git with secret scanning and encryption tools, JSON and YAML tools
Documents pandoc with typst as its PDF engine, graphviz, imagemagick
Browser agent-browser with a headless Chromium, so an agent can open and check a web page it built
Languages Python with uv, Node.js, C and C++ build tools, a Rust installer; Go on request at build time
Cloud the AWS CLI (for signing in to Amazon Bedrock) and an MCP server for Gitea

The README lists every tool. Run pi-devbox-version in a running container to see the exact versions it was built with.

Two flavours

Tag Contains
joakimp/pi-devbox:latest, :vX.Y.Z the image described above
joakimp/pi-devbox:latest-studio, :vX.Y.Z-studio the same, plus pi-studio: a browser workspace attached to the pi session, for editing prompts, previewing Markdown with maths and diagrams, and running REPLs

Both are published for amd64 and arm64.

How it is built

The image is built in two stages, so a release that changes only our components does not rebuild the operating system and tools.

  1. The base image holds the operating system, developer tools and the MemPalace client. Its tag is a hash of the files it is built from. CI rebuilds it only when one of those files changes, and otherwise reuses the existing base.
  2. The variant image starts from the base and adds pi, its packages and our components. Every release builds it.

Versions are chosen on purpose

pi, pi-observational-memory, pi-atelier and MemPalace are pinned to exact versions in the repository. Moving to a new upstream version is a deliberate, reviewable change, not a side effect of whatever was published the morning CI ran. Each pin exists because a new upstream version once broke something here. The other components follow their main branches, but CI resolves each one to an exact commit before the build.

Every image describes itself

Each image records what went into it in two places:

  • Labels on the image: the release, the source commit, and the commit or version of each component. docker inspect shows them.
  • A build manifest inside the image, /etc/pi-devbox/build-manifest.json, written from what was actually installed: the checked-out commit of each component and the version that pi and MemPalace report. pi-devbox-version prints it in readable form.

A running container can therefore always say exactly what it is. The fleet's first-boot checks start from this manifest.

From a tag to a published image

Releases are cut by pushing a version tag. Gitea Actions then runs the pipeline:

flowchart LR
  tag["Version tag"] --> lint["Lint gate"]
  lint --> res["Resolve<br/>versions"]
  res --> base["Base: reuse<br/>or rebuild"]
  base --> smoke["Smoke tests"]
  smoke --> build["Build and push<br/>amd64 + arm64"]
  • Lint gate: checks the repository's shell scripts, that the changelog names every component the build would change, and the Dockerfile's build directives, before anything expensive starts. A separate lint workflow on every push also checks the workflows and the baked skills.
  • Resolve versions: turns every component reference into an exact version or commit.
  • Smoke tests: start the built image and check it: that pi and MemPalace are at the intended versions, that the tools are present, and that the container deploys its extensions and skills.
  • Build and push: publishes both architectures to Docker Hub. The core and -studio flavours have separate smoke and build jobs, so a failure in one does not hold back the other.

Starting a container

The container starts as root and does two things before handing over:

  • It matches your user ID. It changes the in-container user's numeric user and group IDs to match the owner of the mounted /workspace, then drops to that user. Files the agent writes appear on the host with your normal ownership.
  • It restores the parts of a home directory that a new container lacks: default pi settings merged into your existing ones, the fleet's shared extensions linked in, helper commands from a mounted cli_utils checkout put on the path, and SSH settings for reaching the host and the LAN (see Fleet operations).

What survives a recreate

Kind of storage Examples Survives a recreate
Host directories mounted in /workspace, the read-only ~/.ssh, the shared fleet extensions yes, they are host files
Named Docker volumes ~/.pi (pi's configuration and session history), shell history, editor and uv data yes, unless the volumes are deleted
The palace the central palace on its server, or a mounted local one yes, it is outside the container
Everything else packages installed by hand, files written elsewhere in the home directory no

Because session history lives in ~/.pi, observational memory survives a recreate too. After a recreate, scripts/recreate-sanity-check.sh from the repository checks that the expected image is running, the volumes came back and pi's wiring is intact.

Sources

  • pi-devbox README.md: "What's inside", "Image variants", "Volumes and persistence", "Build pipeline", "Build provenance", "Version pins", "Post-recreate sanity check"
  • pi-devbox entrypoint.sh, .gitea/workflows/docker-publish.yml, scripts/smoke-test.sh
  • pi docs/security.md