Skip to main content

Installing system packages with packages.apt

Pai supports installing Debian/Ubuntu system packages at session startup via packages.apt on an Agent. This covers CLI tools (ffmpeg, poppler-utils, git) and shared libraries that cannot be installed with pip or npm.

How it works

When packages.apt is non-empty, the controller injects a dedicated pai-apt-install init container that runs before the harness starts:

  1. apt-get update refreshes the package index
  2. apt-get download fetches the requested .deb archives
  3. dpkg-deb --extract unpacks each archive into /opt/pai-packages/apt/
  4. The harness container inherits /opt/pai-packages/apt/usr/bin and /opt/pai-packages/apt/usr/sbin on PATH, and /opt/pai-packages/apt/usr/lib on LD_LIBRARY_PATH

The init container runs as root (uid 0). The agent container itself continues to run as non-root (uid 65532) — the root exception is isolated to this platform-controlled init step.

Usage

apiVersion: pai.io/v1
kind: Agent
metadata:
name: pdf-processor
spec:
models:
- anthropic/claude-sonnet-4-6
packages:
apt:
- poppler-utils # provides pdftotext, pdfinfo
- imagemagick # provides convert
pip:
- pypdf
tools:
- type: bash
- type: read
- type: write
system: |
You are a PDF processing agent. Use pdftotext to extract text and
pypdf for structured data. Save results to /workspace/output/.

Once installed, packages are available in bash tool commands:

pdftotext input.pdf -      # extract all text to stdout
pdfinfo input.pdf # metadata: page count, dimensions, author
convert input.pdf[0] page1.png # rasterize first page (imagemagick)

Combining with other package managers

packages.apt works alongside all other package managers. The apt init container runs first, so system libraries are available when pip/npm/go packages are built:

packages:
apt:
- libpq-dev # PostgreSQL headers (needed to build psycopg2)
- ffmpeg # video processing CLI
pip:
- psycopg2 # needs libpq-dev at build time
- moviepy

Limitations

The dpkg-deb --extract approach covers the majority of use cases but has some constraints:

Works wellRequires custom image
CLI tools (ffmpeg, git, curl)Packages that register system services
Shared libraries (libpq, libssl)Packages requiring ldconfig cache updates
Language runtimes (ruby, php)Kernel modules or device drivers
Font packagesPackages with postinst scripts that need a live system

For packages that fall into the "requires custom image" column, build a custom harness image:

FROM pai-harness:latest
USER root
RUN apt-get update && apt-get install -y --no-install-recommends \
your-complex-package && \
ldconfig && \
rm -rf /var/lib/apt/lists/*
USER 65532

Then set PAI_HARNESS_IMAGE=your-registry/pai-harness-custom:latest on the controller.

Security note

The pai-apt-install init container is the only container in an agent pod that runs as root. It has no access to the agent's credentials or network connections — it only writes to the shared /opt/pai-packages emptyDir volume. The agent container itself is always non-root.