docs/deploy/docker.md
Run Paperclip in Docker without installing Node or pnpm locally.
docker compose -f docker/docker-compose.quickstart.yml up --build
Open http://localhost:3100.
Defaults:
3100./data/docker-paperclipOverride with environment variables:
PAPERCLIP_PORT=3200 PAPERCLIP_DATA_DIR=../data/pc \
docker compose -f docker/docker-compose.quickstart.yml up --build
Note: PAPERCLIP_DATA_DIR is resolved relative to the compose file (docker/), so ../data/pc maps to data/pc in the project root.
docker build -t paperclip-local .
docker run --name paperclip \
-p 3100:3100 \
-e HOST=0.0.0.0 \
-e PAPERCLIP_HOME=/paperclip \
-v "$(pwd)/data/docker-paperclip:/paperclip" \
paperclip-local
All data is persisted under the bind mount (./data/docker-paperclip):
The Docker image pre-installs these agent CLIs so their *_local adapters can run inside the container:
claude (Anthropic Claude Code CLI) — claude_localcodex (OpenAI Codex CLI) — codex_localopencode (OpenCode multi-provider CLI) — opencode_localgemini (Google Gemini CLI) — gemini_local (experimental)Pass API keys to enable local adapter runs inside the container:
docker run --name paperclip \
-p 3100:3100 \
-e HOST=0.0.0.0 \
-e PAPERCLIP_HOME=/paperclip \
-e OPENAI_API_KEY=sk-... \
-e ANTHROPIC_API_KEY=sk-... \
-e GEMINI_API_KEY=... \
-v "$(pwd)/data/docker-paperclip:/paperclip" \
paperclip-local
Each adapter reads its provider's standard credentials — for example ANTHROPIC_API_KEY (Claude), OPENAI_API_KEY (Codex), and GEMINI_API_KEY or GOOGLE_API_KEY (Gemini). OpenCode is multi-provider and uses whichever provider key you supply.
Gemini key restrictions: Google requires Gemini API keys to be restricted to the Gemini API (scoped in the Google Cloud console); unrestricted keys are blocked and
gemini_localruns will fail with an auth error. Create a restricted key, or authenticate withgemini auth login(OAuth) and persist~/.geminivia the data volume so the credential survives container restarts.
The image sets GEMINI_SANDBOX=false so the Gemini CLI does not try to launch its own (Docker-in-Docker) sandbox inside the container. The gemini_local adapter already passes --sandbox=none per run, so this env var only matters if you invoke gemini manually inside the container; override it if you have nested-container support and want CLI-level sandboxing.
Without API keys, the app runs normally — adapter environment checks will surface missing prerequisites.