Run PiChamber in Docker
If you already run things in containers, PiChamber fits that habit. The official image is ghcr.io/ryderasking/pichamber and Compose is the supported way to run it. One container holds one server, and everything worth keeping lives in bind mounted directories under ./data so recreating the container never wipes you out.
This post follows the official Docker guide and the install docs. It covers both starting points I see people want. Start fresh with nothing from the host, or bring your dotfiles and projects with you.
What you need
Docker with the Compose plugin, an empty directory for the deployment, and the Compose file from the PiChamber repo:
Copy that file into your empty directory. That directory is now your Compose project. Everything below runs from inside it.
Two things to know before you start. The UI password is required, because the server listens on all container interfaces. And the published port stays on loopback by default, so only your machine can reach it until you opt into LAN access.
Start fresh with no host mounts
This is the default and the safest first run. Docker keeps its own Pi configuration under ./data/pi/agent and you sign in to providers from inside PiChamber. Nothing from your host Pi install is touched.
mkdir -p data/pichamber data/pi/agent data/ssh data/workspaces
export PICHAMBER_UI_PASSWORD="$(openssl rand -base64 24)"
printf 'PiChamber password: %s\n' "$PICHAMBER_UI_PASSWORD"
docker compose up -d
docker compose logs -f
Save that password somewhere real. Then open http://127.0.0.1:32145.
What you have now:
./data/pichamber server config
./data/pi/agent Pi sessions, providers, settings, skills
./data/ssh container SSH keys
./data/workspaces your projects, seen in PiChamber as /home/pichamber/workspaces
Deleting or recreating the container does not remove these directories. That is the whole point. When a new PiChamber release ships, you pull and recreate (more on that below) and ./data carries over.
If you want to pin a version instead of floating on latest, set PICHAMBER_VERSION to an exact release. Production should always pin.
PICHAMBER_VERSION=1.0.1 docker compose up -d
Add projects
The host workspace directory is ./data/workspaces. PiChamber sees it as /home/pichamber/workspaces and adds that Workspaces folder on a fresh Docker install. If you remove it from PiChamber, that choice sticks.
To bring an existing checkout along, copy it under the host directory:
cp -a /path/to/project ./data/workspaces/project
# When your host user is not UID 1000, hand the copy to the container user:
sudo chown -R 1000:1000 ./data/workspaces/project
chown accepts raw numbers, so this works even when no host user is called 1000. Names are just a lookup over /etc/passwd. The kernel only stores numbers, and ls shows the bare number when nothing maps to it. Run id -u to check your own UID. On many desktop Linux installs the first user already is 1000, in which case your files arrive with the right owner and you can skip the chown.
Then in PiChamber pick Add project and add /home/pichamber/workspaces/project.
You can also do it the other way. Pick Clone & add in PiChamber and clone straight into /home/pichamber/workspaces/project. Project level AGENTS.md, .pi/, .agents/, git data, and other dotfiles stay in the mounted workspace either way.
One ownership rule explains most Docker permission errors. After the entrypoint drops privileges it runs as UID 1000 with no capabilities left, so it cannot chown its way out of a bad mount. Pre create each extra host directory and give it 1000:1000 ownership before first start. Docker auto creates a missing host directory as root, which needs the same mkdir -p plus sudo chown -R 1000:1000 treatment. Read only mounts such as skills do not need ownership changes.
Reuse your existing Pi setup
Pi keeps most user level state in ~/.pi/agent. Provider auth, settings, models, sessions, extensions, skills, prompts, themes, global AGENTS.md, system prompt files, trust decisions, keybindings. Docker cannot see any of it unless you copy or mount it. Pick one of three approaches and do not mix them.
| Approach | When it fits |
|---|---|
| Isolated configuration | Server use, or keeping Docker separate from desktop Pi |
| One time copy | Start from your desktop config, then let the two drift apart |
| Live bind mount | You deliberately want host and container sharing one Pi directory |
Never run desktop Pi and Docker PiChamber against the same live agent directory at the same time. Both write auth, sessions, settings, and lock files, and they will step on each other.
Copy your Pi config once
Stop Pi and PiChamber first. Copy on the host so symlinks resolve against the host filesystem, then set ownership for the container user:
mkdir -p data/pi/agent
cp -aL "$HOME/.pi/agent/." ./data/pi/agent/
docker run --rm \
--entrypoint chown \
-v "$PWD/data/pi/agent:/destination" \
ghcr.io/ryderasking/pichamber:latest \
-R 1000:1000 /destination
The -L flag matters. It copies symlink targets, which counts when files under ~/.pi/agent point into a separate dotfiles repo. Then start normally:
docker compose up -d
After import the Docker copy is independent. Later desktop changes do not appear in Docker, and Docker changes do not alter the host copy. That separation is the reason I recommend this path over live mounting.
Two warnings from the guide, both earned. The copy can include provider tokens from auth.json, so protect ./data/pi/agent, never commit it, and keep it out of support archives. And package caches or binaries under bin/, npm/, or git/ may have been built for your host OS. If an imported resource fails on a different OS or CPU arch, delete that cache inside ./data/pi/agent and reinstall the package from PiChamber.
Share the host Pi directory live
If you truly want one shared directory, create docker-compose.host-pi.yml next to the main Compose file:
services:
pichamber:
volumes:
- ${PICHAMBER_HOST_PI_AGENT_DIR:?Set PICHAMBER_HOST_PI_AGENT_DIR}:/home/pichamber/.pi/agent
export PICHAMBER_HOST_PI_AGENT_DIR="$HOME/.pi/agent"
docker compose \
-f docker-compose.yml \
-f docker-compose.host-pi.yml \
up -d
Only do this when the host files are writable by UID 1000. The container prepares the mount root for UID 1000, which can change ownership of your host ~/.pi/agent directory on Linux. When your host user is not UID 1000, the one time copy above is safer.
Symlinks bite here too. A symlink in the mounted agent directory still points at its original absolute target, which is normally absent inside the container. Either use the copy (it follows symlinks) or mount the dotfiles repo at the same absolute path inside the container. That extra mount exposes everything under it to PiChamber, so prefer the copy unless live sync is required.
Bring the extra skills directory and env backed keys
Two more pieces of your setup live outside ~/.pi/agent.
Pi also discovers skills under ~/.agents/skills. To make them available without letting Docker change them, add a read only volume to an override:
services:
pichamber:
volumes:
- ${HOME}/.agents/skills:/home/pichamber/.agents/skills:ro
Project level .agents/skills directories already travel with their workspace and need no extra mount.
Copying models.json does not copy environment variables it references, such as $ANTHROPIC_API_KEY. Pass each one explicitly rather than mounting the whole host environment:
services:
pichamber:
environment:
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?Set ANTHROPIC_API_KEY}
Never commit secrets to the Compose file or .env. Commands referenced with !command in Pi config must also exist inside the image. Host only commands are unavailable.
Pair your other devices
Open the server in your browser first and use Add a device there. From the Compose directory you can also print a pairing code:
docker compose exec pichamber bun packages/web/bin/cli.js connect-url --qr
Scan it from the device you want to connect. Pairing grants access to the server workspace, so only pair devices you control and revoke access when a device stops being trusted.
Back up, update, repeat
Back up with the container stopped so the copy is consistent:
docker compose stop
cp -a data "data-backup-$(date +%Y%m%d)"
docker compose start
At minimum protect data/pi/agent, data/pichamber, and data/workspaces. The SSH directory holds a private key and is sensitive too.
Update by pulling and recreating. Never run pichamber update inside the image.
docker compose pull
docker compose up -d
Running more than one container? Give each Compose project its own host port and data directory:
mkdir -p data-{1,2}/{pichamber,pi/agent,ssh,workspaces}
PICHAMBER_UI_PASSWORD="first-password" PICHAMBER_HOST_PORT=32146 PICHAMBER_DATA_DIR=./data-1 docker compose -p pichamber-1 up -d
PICHAMBER_UI_PASSWORD="second-password" PICHAMBER_HOST_PORT=32147 PICHAMBER_DATA_DIR=./data-2 docker compose -p pichamber-2 up -d
Never point two running containers at the same Pi agent directory or workspace.
The security small print
The container can read and write every mounted file, so mount only the workspaces and Pi resources this server should use. Never mount the Docker socket, your whole home directory, or unrelated credential directories such as ~/.aws or ~/.gnupg. Containers share the host kernel. Docker limits filesystem and process access, but it is not a security boundary for hostile code. The security docs cover PiChamber's single owner model.
Start isolated, copy your config once if you miss your desktop setup, and only reach for live mounts when you know why you need them. That order has the fewest surprises.