# OpenClaw + PAI Container Image # Builds a self-contained OpenClaw + PAI (Claude Code) environment. # # Required at runtime: # OPENCLAW_GATEWAY_TOKEN — gateway auth token (container won't start without it) # ANTHROPIC_API_KEY — Claude Code API key # # Optional (all have sane defaults): # See ENV block below. FROM ubuntu:24.04 # ── System setup ────────────────────────────────────────────────── ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get upgrade -y && apt-get dist-upgrade -y && \ apt-get install -y \ fish curl git unzip gettext-base \ libnss3 libatk1.0-0t64 libatk-bridge2.0-0t64 \ libcups2t64 libgtk-3-0t64 libgbm1 && \ apt-get clean && rm -rf /var/lib/apt/lists/* # ── Agent user ──────────────────────────────────────────────────── RUN usermod -l agent ubuntu && \ groupmod -n agent ubuntu && \ usermod -d /home/agent -m agent && \ chsh -s /usr/bin/fish agent USER agent WORKDIR /home/agent # ── Fish shell config ───────────────────────────────────────────── RUN mkdir -p /home/agent/.config/fish/conf.d && \ printf '%s\n' \ '# PATH' \ 'fish_add_path /home/agent/.npm-global/bin' \ 'fish_add_path /home/agent/.bun/bin' \ '' \ '# Aliases' \ "alias l 'ls -lah'" \ "alias ll 'ls -alF'" \ "alias la 'ls -A'" \ > /home/agent/.config/fish/conf.d/env.fish && \ printf '%s\n' \ '# PAI alias — resolves PAI_DIR at runtime so it works with any volume mount' \ "alias pai 'bun \$PAI_DIR/PAI/Tools/pai.ts'" \ > /home/agent/.config/fish/conf.d/pai.fish && \ chown -R agent:agent /home/agent/.config/fish USER root RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ apt-get install -y nodejs && \ apt-get clean && rm -rf /var/lib/apt/lists/* USER agent RUN npm config set prefix /home/agent/.npm-global ENV PATH="/home/agent/.npm-global/bin:/home/agent/.bun/bin:${PATH}" # ── Bun ─────────────────────────────────────────────────────────── RUN curl -fsSL https://bun.sh/install | bash # ── OpenClaw ────────────────────────────────────────────────────── RUN npm install -g openclaw && \ rm -rf /home/agent/.npm/_cacache /home/agent/.npm/_logs /home/agent/.npm/_npx # Generate default config RUN openclaw gateway --allow-unconfigured &>/tmp/oc-init.log & \ sleep 5 && kill %1 2>/dev/null; \ rm -rf /tmp/oc-init.log /tmp/node-compile-cache; true # Apply env-var template config COPY --chown=agent:agent openclaw.json /home/agent/.openclaw/openclaw.json # ── Claude Code ─────────────────────────────────────────────────── RUN npm install -g @anthropic-ai/claude-code && \ rm -rf /home/agent/.npm/_cacache /home/agent/.npm/_logs /home/agent/.npm/_npx # ── PAI v4.0.3 ──────────────────────────────────────────────────── RUN git clone --depth=1 \ https://github.com/danielmiessler/Personal_AI_Infrastructure.git \ /tmp/pai-src && \ cp -r /tmp/pai-src/Releases/v4.0.3/.claude /home/agent/.claude && \ rm -rf /tmp/pai-src # Apply env-var template settings COPY --chown=agent:agent claude-settings.json /home/agent/.claude/settings.json # Patch voice server URL to use VOICE_SERVER_URL env var RUN grep -rl 'localhost:8888' /home/agent/.claude/PAI/ /home/agent/.claude/CLAUDE.md | \ xargs sed -i 's|http://localhost:8888|${VOICE_SERVER_URL:-http://localhost:8888}|g' # Build CLAUDE.md from template (uses dummy values; real values come from env at runtime) RUN PAI_DIR=/home/agent/.claude \ PAI_CONFIG_DIR=/home/agent/.config/PAI \ ANTHROPIC_BASE_URL=http://localhost:4000 \ ANTHROPIC_API_KEY=build-time-placeholder \ ANTHROPIC_MODEL=claude-sonnet-4-6 \ bun /home/agent/.claude/PAI/Tools/BuildCLAUDE.ts && \ rm -rf /home/agent/.bun/install/cache /tmp/node-compile-cache # ── Clean workspace for image snapshot ─────────────────────────── # Only AGENTS.md and BOOTSTRAP.md — no pre-filled identity files. # The agent will fill in SOUL.md, USER.md, IDENTITY.md, etc. during onboarding. COPY --chown=agent:agent workspace-seed/AGENTS.md /home/agent/.openclaw/workspace/AGENTS.md COPY --chown=agent:agent workspace-seed/BOOTSTRAP.md /home/agent/.openclaw/workspace/BOOTSTRAP.md # ── Scrub remaining build artifacts ────────────────────────────── RUN rm -f /home/agent/.bash_history /home/agent/.sh_history \ /root/.bash_history /root/.sh_history && \ find /home/agent/.config/fish -name '*_history' -delete 2>/dev/null; true # ── Image-state snapshots (first-run initialization sources) ────── RUN cp -r /home/agent/.openclaw /home/agent/.openclaw.image && \ cp -r /home/agent/.claude /home/agent/.claude.image && \ cp /home/agent/.claude/settings.json /home/agent/.claude.image/settings.base.json # ── Entrypoint ──────────────────────────────────────────────────── COPY --chown=agent:agent entrypoint.sh /home/agent/entrypoint.sh RUN chmod +x /home/agent/entrypoint.sh # ── Default environment ─────────────────────────────────────────── # All persistent state lives under /home/agent/vol (the expected PVC mount point). # OpenClaw ENV OPENCLAW_STATE_DIR=/home/agent/vol/.openclaw ENV OPENCLAW_GATEWAY_PORT=18800 ENV OPENCLAW_GATEWAY_BIND=lan ENV OPENCLAW_DEFAULT_MODEL=litellm/claude-sonnet-4-6 ENV OPENCLAW_WORKSPACE=/home/agent/vol/.openclaw/workspace ENV LITELLM_BASE_URL=http://localhost:4000 # TLS (off | auto | custom) ENV OPENCLAW_TLS=off # PAI / Claude Code ENV PAI_DIR=/home/agent/vol/.claude ENV CLAUDE_CONFIG_DIR=/home/agent/vol/.claude ENV PAI_CONFIG_DIR=/home/agent/vol/.config/PAI ENV PROJECTS_DIR=/home/agent/vol/repositories ENV ANTHROPIC_BASE_URL=http://localhost:4000 ENV ANTHROPIC_MODEL=claude-sonnet-4-6 ENV ANTHROPIC_SMALL_FAST_MODEL=claude-haiku-4 ENV VOICE_SERVER_URL=http://localhost:8888 ENV VOICE_SERVER_PORT=8888 # ── Ports ───────────────────────────────────────────────────────── EXPOSE 18800 EXPOSE 8888 # ── Run as agent ────────────────────────────────────────────────── USER agent CMD ["/home/agent/entrypoint.sh"]