joakimp 34cae2a1d2
Publish Docker Image / smoke (push) Successful in 2m18s
Publish Docker Image / publish (push) Successful in 12m59s
Publish Docker Image / update-description (push) Successful in 11s
Cut v0.75.5b — fix cache-hit silent same-bytes regression
ALL FOUR releases v0.74.0 -> v0.75.5 had been shipping the same image
bytes due to a Docker layer-cache hit on the bare 'npm install -g
@earendil-works/pi-coding-agent' command (when PI_VERSION=latest).
The command string is identical across builds, so the layer-hash is
identical, so registry buildcache (cache-from/cache-to) silently
reuses the layer from whatever pi version was current when the cache
was first populated.

Verification: docker manifest inspect joakimp/pi-devbox:vX.Y.Z showed
identical SHA256 digests on both linux/amd64 and linux/arm64 for
v0.74.0, v0.75.3, v0.75.4, v0.75.5. Users on :latest were getting
whatever pi version was baked into the v0.74.0 build.

DISCOVERED 2026-05-23 by user trying to update pi-devbox on MBP-M1
and seeing pi 0.74.0 reported despite pulling v0.75.5.

CHANGES

.gitea/workflows/docker-publish.yml — both smoke and publish jobs
get a new 'Resolve PI_VERSION from tag' step that strips the leading
'v' and any trailing letter suffix from github.ref_name. Result is
passed as a build-arg to docker/build-push-action so the npm install
layer's hash includes the concrete version, forcing cache miss when
pi bumps.

scripts/smoke-test.sh — new run_expect helper that asserts pi
--version contains the EXPECTED_PI_VERSION env var. Smoke job sets
this from the resolve step output. Would have caught this regression
on v0.75.3.

Dockerfile — comment block above ARG PI_VERSION=latest documenting
the cache-hit footgun. The 'if latest' branch in the install RUN is
preserved for local dev convenience but never fires in CI now.

AGENTS.md — new convention bullet explaining the cache-hit class of
bug and noting the latent same-bug in opencode-devbox's with-pi
variants (currently masked by OPENCODE_VERSION bumps; will manifest
when cutting a vN.N.Nb-style opencode-version-unchanged release that
only bumps pi).

CHANGELOG.md — full entry under v0.75.5b describing the recovery,
the silent-failure mechanism, and the verification steps.

NO IMAGE-CONTENT CHANGES vs v0.75.5 INTENT. This build produces the
actual pi 0.75.5 image content that v0.75.5 was supposed to ship.

NEXT FOLLOWUP (parked, not in this commit)

opencode-devbox should get the same workflow change for its
build-variant-with-pi and build-variant-omos-with-pi jobs. Currently
masked because every release also bumps OPENCODE_VERSION which
invalidates the cache, but that masking would fail on a pi-only bump
release.
2026-05-23 22:10:08 +02:00
2026-05-14 19:57:17 +02:00
2026-05-14 19:57:17 +02:00

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

Inherited from opencode-devbox:base-latest:

  • 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

Added by pi-devbox:

  • pi (@earendil-works/pi-coding-agent) — baked at /usr/bin/pi, version pinned at build time
  • 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
  • mempalace bridge — auto-symlinked MCP extension so pi reads/writes the same palace as opencode

The entrypoint deploys 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
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, …

When the upstream pi npm package cuts a new version, this image is rebuilt and re-tagged to match.


Building from source

If you want to pin a specific pi version, change the base image, or hack on the Dockerfile:

git clone https://gitea.jordbo.se/joakimp/pi-devbox
cd pi-devbox

# Edit Dockerfile or override via build args:
docker compose build \
  --build-arg PI_VERSION=0.73.0 \
  --build-arg BASE_IMAGE=joakimp/opencode-devbox:base-latest

docker compose up -d

Build args supported:

Arg Default Effect
BASE_IMAGE joakimp/opencode-devbox:base-latest Parent image — set to joakimp/opencode-devbox:base-<sha> for reproducible builds
PI_VERSION latest npm version of @earendil-works/pi-coding-agent
PI_TOOLKIT_REF main Git ref for pi-toolkit
PI_EXTENSIONS_REF main Git ref for pi-extensions

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.


  • opencode-devbox — the base image. Use this if you want both opencode and pi (it has a latest-with-pi variant) or just opencode.
  • pi-toolkit — keybindings, env loader, settings template. Cloned into /opt/pi-toolkit at image build time and install.sh runs on container start.
  • pi-extensions — extension source. Same install pattern.
  • mempalace-toolkit — MemPalace bring-up. The mempalace.ts extension 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.

S
Description
pi coding-agent container built on opencode-devbox base
Readme 248 KiB
Languages
Shell 76.3%
Dockerfile 23.7%