Custom base image

Orka builds a base image (orka-base) from an embedded Dockerfile.base before building the agent image on top of it. To replace that base, place your own Dockerfile.base at:

~/.config/orka/Dockerfile.base

When this file is present, orka uses it instead of the embedded default. To revert to the default temporarily, rename or remove the file.

The base image is built with the Docker layer cache and is shared across all harnesses, so it is only rebuilt when it changes. The agent layer on top — which installs pi, claude, or codex — rebuilds independently.

Why you would do this

  • Your organization requires a specific base OS (RHEL, Ubuntu, Alpine).
  • You want to add tools or configuration to every agent session without writing a preset for them.
  • You want to strip agent-browser and Chromium from the image to reduce size.

What the default base provides

The embedded Dockerfile.base installs:

  • A broad set of Linux development tools (git, curl, jq, ripgrep, fd-find, fzf, gh, python3, and others)
  • Bun (used by the pi and claude harnesses to install the agent)
  • agent-browser and its Chromium dependency (used by the pi harness for browser tool calls)

Minimum requirements per harness

Your custom base must provide whatever the agent harness layer needs at build time. The harness layer does not install a package manager — it assumes the base has what is needed.

HarnessRequired in base
pibun at /usr/local/bin/bun
claudebun at /usr/local/bin/bun
codexcurl, jq, tar

All harnesses also require groupadd, useradd, and bash. These are present on any standard Linux distribution.

The AGENT_BROWSER_EXECUTABLE_PATH convention

agent-browser install downloads Chrome for Testing into $HOME/.agent-browser/browsers of whichever user runs it — root during the image build, which the runtime user cannot read. The default base therefore installs under a throwaway HOME, moves the download to /opt/browser-cache, and points every user at it:

RUN HOME=/tmp/agent-browser-install agent-browser install && \
    mv /tmp/agent-browser-install/.agent-browser/browsers/* /opt/browser-cache/ && \
    ln -s "$(find /opt/browser-cache -maxdepth 2 -type f -name chrome | head -n 1)" /opt/browser-cache/chrome && \
    chmod -R a+rX /opt/browser-cache

ENV AGENT_BROWSER_EXECUTABLE_PATH="/opt/browser-cache/chrome"

If your custom base includes agent-browser, do the same. Any readable path works as long as AGENT_BROWSER_EXECUTABLE_PATH names the Chrome binary. If your base omits agent-browser, omit the variable.

Example: RHEL / dnf

FROM registry.access.redhat.com/ubi9/ubi:latest

RUN dnf install -y \
      bzip2 \
      curl \
      git \
      jq \
      python3 \
      ripgrep \
      tar \
      unzip \
    && dnf clean all

ARG BUN_VERSION=1.3.14

RUN set -eux && \
    ARCH=$(uname -m) && \
    case "$ARCH" in \
      x86_64)  TARGET="bun-linux-x64" ;; \
      aarch64) TARGET="bun-linux-aarch64" ;; \
      *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
    esac && \
    curl -fsSL "https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/${TARGET}.zip" \
      -o /tmp/bun.zip && \
    unzip /tmp/bun.zip -d /tmp && \
    mv "/tmp/${TARGET}/bun" /usr/local/bin/bun && \
    chmod +x /usr/local/bin/bun && \
    rm -rf /tmp/bun.zip "/tmp/${TARGET}" && \
    ln -sf /usr/local/bin/bun /usr/local/bin/node && \
    ln -sf /usr/local/bin/bun /usr/local/bin/npm && \
    ln -sf /usr/local/bin/bun /usr/local/bin/npx

This gives you a RHEL-based image suitable for all three harnesses. It omits agent-browser and Chromium — pi will still work but browser tool calls will fail at runtime.

Example: Debian without agent-browser

Start from the same Debian base but skip the agent-browser and Chromium layers:

FROM debian:bookworm-slim

ARG BUN_VERSION=1.3.14

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
      curl \
      git \
      jq \
      python3 \
      ripgrep \
      unzip \
    && rm -rf /var/lib/apt/lists/*

RUN set -eux && \
    ARCH=$(uname -m) && \
    case "$ARCH" in \
      x86_64)  TARGET="bun-linux-x64" ;; \
      aarch64) TARGET="bun-linux-aarch64" ;; \
      *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
    esac && \
    curl -fsSL "https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/${TARGET}.zip" \
      -o /tmp/bun.zip && \
    unzip /tmp/bun.zip -d /tmp && \
    mv "/tmp/${TARGET}/bun" /usr/local/bin/bun && \
    chmod +x /usr/local/bin/bun && \
    rm -rf /tmp/bun.zip "/tmp/${TARGET}" && \
    ln -sf /usr/local/bin/bun /usr/local/bin/node && \
    ln -sf /usr/local/bin/bun /usr/local/bin/npm && \
    ln -sf /usr/local/bin/bun /usr/local/bin/npx

Verifying

Use --dry-run to confirm orka is picking up your file:

orka --dry-run

The base image build command will show --file pointing at a temporary copy of your Dockerfile.base. If you see the embedded default instead, check that the file is at ~/.config/orka/Dockerfile.base.