Skip to content

Repository files navigation

paddock

Launch coding agents (or a plain shell) in isolated Docker containers, with the current working directory mounted as the workspace.

PyPI version

Python versions

MIT Licence

Caution!

Pre-release software — here be dragons. APIs, configuration formats, and CLI flags may change without notice in future versions. Bugs and crashes are possible.

Overview

paddock assembles and executes a docker run command from a layered configuration system. Sources are merged in ascending priority — later sources overwrite earlier ones:

  1. Project-level TOML (<workdir>/.paddock/config.toml)
  2. User-level TOML (~/.config/paddock/config.toml)
  3. [projects."<path>"] overrides in the user TOML
  4. Extra TOML file via PADDOCK_CONFIG_FILE env var, or via the --config-file CLI flag — the CLI path replaces the env path, so the two are one source, not two
  5. PADDOCK_* environment variables
  6. CLI flags

volumes entries are additive per host path — the same host path set by a higher-priority source replaces the earlier mapping.

The project-level file is off by default (blocked) — enable it in the user file's [config.allowlist]; see project-level configuration and the allowlist.

Requirements

  • Python 3.12+
  • Docker (CLI must be available on PATH)

Installation

pip install phx-paddock

Or with uv:

uv tool install phx-paddock

Quick Start

Drop into a plain bash shell inside the current directory:

paddock --image=ubuntu:24.04 --agent=false

Run Claude Code in an isolated container:

paddock --image=my-claude-image --agent=claude

Print the assembled docker run command without executing it:

paddock --image=ubuntu:24.04 --agent=false --dry-run

Configuration

TOML files

Place a config.toml at ~/.config/paddock/ (user-level) or <project>/.paddock/ (project-level). Both are optional, and the project-level file is off by default until you opt in from your user config:

[config.allowlist]
project_toml = true

true is the blanket grant; a list such as project_toml = ["volumes"] permits only the keys it names. See project-level configuration and the allowlist for what each grant hands a committed file.

A config file looks like this:

agent  = "claude"
image  = "my-claude-image:latest"
network = "my-docker-network"

[volumes]
"/host/path" = "/container/path:ro"

[build]
dockerfile = "images/Dockerfile"
context    = "."
policy     = "daily"

[build.args]
AGENT          = "claude"
PYTHON_VERSION = "3.13"

Config fields

Field Type Description
agent string or false Agent key ("claude") or false for shell
image string Docker image to run (required)
network string (optional) Docker network to attach the container to
volumes {host: container} map Extra bind-mounts; container path may end in :ro or :rw (bare path defaults to :ro)
build sub-table (optional) Image auto-build settings (see below)

Build sub-table

Field Type Description
dockerfile string Path to the Dockerfile (required if build table is present)
context string (optional) Docker build context path
policy "always" / "daily" / "if-missing" / "weekly" When to rebuild the image
args {name: value} map (optional) Build-time --build-arg values

Environment variables

Six config fields can be set via an environment variable, by uppercasing the field name and prefixing it with PADDOCK_. Nested keys are joined with _:

PADDOCK_AGENT=claude
PADDOCK_BUILD_CONTEXT=.
PADDOCK_BUILD_DOCKERFILE=images/Dockerfile
PADDOCK_BUILD_POLICY=daily
PADDOCK_IMAGE=my-claude-image
PADDOCK_NETWORK=my-docker-network

PADDOCK_CONFIG_FILE=/path/to/extra.toml   # loads an additional TOML file

volumes and build.args have no environment-variable form — set them in a TOML file, or pass --volume / --build-args-KEY=VALUE on the command line. PADDOCK_BUILD_ARGS is ignored rather than rejected. Any other unrecognised PADDOCK_* name is a fatal config error, so a typo stops the run:

[env:foo] Unexpected key "foo".

CLI flags

paddock [FLAGS] [--] [COMMAND...]

--agent AGENT                Agent key (e.g. "claude") or "false" for a shell
--build-args-KEY=VALUE        Build-time ARG (repeatable)
--build-context PATH         Docker build context
--build-dockerfile PATH      Path to Dockerfile
--build-policy POLICY        Build policy (always|daily|if-missing|weekly)
--config-file PATH           Load an additional TOML config file
--dry-run                    Print the docker command and exit without running it
--image IMAGE                Docker image
--network NETWORK            Docker network
--quiet                      Suppress all logging and the docker command printout
--volume HOST:CONTAINER[:MODE]  Extra bind-mount (repeatable)
--workdir PATH               Host path to use as the workspace (default: CWD)

--workdir is resolved to an absolute real path — symlinks followed — before it is used for the [projects] lookup and for the mounts.

paddock exits with the container's exit status; --dry-run exits 0 and a config error exits 1.

Everything after the first positional argument (or after --) is passed as the container command:

paddock claude --allow-dangerously-skip-permissions --continue
paddock --image=my-claude-image -- --allow-dangerously-skip-permissions --continue

Agents

claude

Runs claude inside the container. Mounts ~/.claude from the host to /root/.claude:rw so authentication and configuration persist between sessions.

false (shell)

Runs /bin/bash. Useful for exploring the container environment or running ad-hoc commands without a coding agent.

Adding agents

Additional agents can be registered via the paddock.agents entry-point group in any installed package:

[project.entry-points."paddock.agents"]
my-agent = "mypackage.agents:MyAgent"

Each agent must subclass paddock.agents.BaseAgent and implement get_command() and get_volumes().

Docker Image

A ready-to-use Dockerfile is included in images/. It installs Python (via the deadsnakes PPA), Node.js, and the selected coding agent.

Build arguments:

ARG Default Description
UBUNTU_VERSION 24.04 Ubuntu base image tag
AGENT none claude or none
NODE_VERSION 22 Node.js major version
PYTHON_VERSION 3.13 Python version (installed from deadsnakes)

Build the image manually:

docker build \
  --build-arg AGENT=claude \
  -t my-claude-image \
  -f images/Dockerfile .

Or set a [build] table in your config and let paddock build it automatically according to your chosen policy.

Licence

MIT — see LICENCE.txt.

About

Containerised sandbox for coding agents

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages