Docker Deployment: Run PocketPaw in Containers

PocketPaw ships with a production-ready Dockerfile and docker-compose.yml in the repository root. The Docker image includes all extras ([all]), Playwright Chromium, tesseract OCR, Node.js with the Claude Code CLI and Codex CLI — everything works out of the box.

Quick Start

Terminal window
# Clone the repo
git clone https://github.com/pocketpaw/pocketpaw.git
cd pocketpaw
# Copy the env template and fill in your keys
cp .env.example .env
# Build and start
docker compose up -d

The dashboard is available at http://localhost:8888.

Warning

Docker’s bridge networking means localhost auth bypass doesn’t work inside a container. You’ll need to log in with the access token.

Get the access token and paste it into the login page:

Terminal window
docker exec pocketpaw cat /home/pocketpaw/.pocketpaw/access_token

Dockerfile Overview

The Dockerfile uses a multi-stage build for a lean runtime image:

Builder stage (python:3.12-slim):

  • Installs build dependencies (gcc, python3-dev, git)
  • Creates a virtual environment and installs pocketpaw[all]
  • Downloads Playwright Chromium

Node stage (node:22-slim):

  • Installs Claude Code CLI (@anthropic-ai/claude-code) and Codex CLI (@openai/codex) globally

Runtime stage (python:3.12-slim):

  • Installs only runtime system deps (tesseract, Chromium shared libs, curl)
  • Copies Node.js binary and global packages from the node stage (no curl | bash)
  • Copies the venv and Playwright browsers from the builder
  • Creates a /home/pocketpaw/workspace directory for agent-created files
  • Runs as a non-root pocketpaw user
  • Exposes port 8888 with a healthcheck
Info

The container binds to 0.0.0.0:8888 by default (set via POCKETPAW_WEB_HOST and POCKETPAW_WEB_PORT environment variables in the Dockerfile).

Tip

The Node.js layer (runtime + Claude Code CLI + Codex CLI) adds roughly 200 MB to the image. If you don’t need the claude_agent_sdk or codex_cli Docker-native CLI backends and want a smaller image, you can remove the node stage and the corresponding COPY --from=node lines in the Dockerfile.

Docker Compose

The docker-compose.yml defines three services. Only the main pocketpaw service starts by default — Ollama and Qdrant are behind Compose profiles.

Default (PocketPaw only)

Terminal window
docker compose up -d

With Ollama (local LLM)

Option A: Ollama in Docker (via Compose profile)

Terminal window
docker compose --profile ollama up -d

Set the Ollama host to the container service name in your .env:

Terminal window
POCKETPAW_OLLAMA_HOST=http://ollama:11434

Then pull the models you need:

Terminal window
docker compose exec ollama ollama pull llama3.2
docker compose exec ollama ollama pull nomic-embed-text
Tip

For GPU passthrough with NVIDIA, uncomment the deploy.resources.reservations block in docker-compose.yml.

Option B: Ollama on the host

If Ollama is already running on your host machine (outside Docker), the container can reach it via host.docker.internal — this is configured automatically in docker-compose.yml via extra_hosts. Set in your .env:

Terminal window
POCKETPAW_OLLAMA_HOST=http://host.docker.internal:11434
Warning

The default http://localhost:11434 won’t work from inside a container — localhost inside the container refers to the container itself, not your host machine.

With Qdrant (vector memory)

Terminal window
docker compose --profile qdrant up -d

Qdrant is needed for Mem0 semantic memory. It exposes port 6333 for the REST API.

All services

Terminal window
docker compose --profile ollama --profile qdrant up -d

Environment Variables

All configuration is passed via environment variables. Copy .env.example to .env and fill in the values you need:

Terminal window
cp .env.example .env

The .env.example file is organized by section (LLM, channels, memory, tools, security) and documents every available POCKETPAW_ variable. See the Configuration reference for full details.

You can also change the host port:

Terminal window
POCKETPAW_PORT=9000 docker compose up -d

Persistent Data

Two volumes are mounted:

Config & memory — named volume pocketpaw-data at /home/pocketpaw/.pocketpaw:

  • Configuration (config.json, secrets.enc)
  • Session history
  • Memory data (file-based and Mem0)
  • OAuth tokens
  • Audit logs
  • MCP server configs
  • Skills

Workspace — bind mount ./workspace at /home/pocketpaw/workspace:

  • Files created by the agent via Write, Bash, or other tools
  • Directly accessible on the host at ./workspace/ next to your docker-compose.yml
  • The POCKETPAW_FILE_JAIL_PATH env var is set automatically in the image

Data survives docker compose down and docker compose up cycles. To fully reset, remove the named volume:

Terminal window
docker compose down -v

To customize the workspace path on the host, edit the bind mount in docker-compose.yml:

volumes:
- /your/preferred/path:/home/pocketpaw/workspace

Managing the Container

Terminal window
# View logs
docker compose logs -f pocketpaw
# Restart
docker compose restart pocketpaw
# Stop everything
docker compose down
# Rebuild after pulling updates
git pull && docker compose build && docker compose up -d

Browser Automation

Playwright Chromium is included in the Docker image along with all required shared libraries. Browser tools work out of the box inside the container — no additional setup needed.

Claude Code OAuth Support

The Docker image includes the Claude Code CLI pre-installed. You can authenticate using an OAuth token from your Claude Max or Pro plan instead of a separate API key.

Option 1: OAuth token (recommended for remote/Coolify deployments):

Terminal window
# Generate a long-lived token on your local machine
claude setup-token
# Add to .env
POCKETPAW_CLAUDE_SDK_PROVIDER=claude_code
POCKETPAW_CLAUDE_CODE_OAUTH_TOKEN={"accessToken":"sk-ant-oat01-...","refreshToken":"sk-ant-ort01-...","expiresAt":"2027-..."}

Option 2: CLI interactive login (persisted in a volume):

Mount a volume to /home/pocketpaw/.claude, exec into the container, run claude, and complete the browser-based login once. Credentials persist across restarts.

See the Discord Docker guide for detailed instructions on both options.

Discord-Only Deployment

For a lightweight, headless Discord bot without the web dashboard, use the dedicated setup in deploy/discord/. It has a smaller image (no Playwright/Chromium), its own Dockerfile, and compose file optimized for single-channel operation.

See Discord Docker Deployment for the full guide.

Limitations

  • Desktop tools (pyautogui) require a display server and won’t work in a headless container. They fail gracefully at invocation time.
  • WhatsApp Personal mode (neonize QR pairing) requires a persistent session. The named volume handles this, but the QR code must be scanned via the web dashboard on first setup.
  • Copilot SDK backend requires the copilot Go binary which is not bundled in the image. Install it manually if needed.