Skip to main content

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

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.

Metadata

Release files for phx-paddock 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for phx-paddock 0.4.0
File Size Uploaded
phx_paddock-0.4.0.tar.gz 27.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for phx-paddock 0.4.0
File Interpreter ABI Platform
phx_paddock-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 65.9 kB

Release files / phx_paddock-0.4.0.tar.gz

Download URL phx_paddock-0.4.0.tar.gz
Size 27.9 kB
Tags Source
SHA-256 checksum
How to use checksums
550e1de570daa448a1edb22c910026dbf0ea5014375189c1611fe1ca84c984fe
BLAKE2b-256 checksum
How to use checksums
a041f880f508ee159400bf863ebd961d962a3f3d1c7bccbd9bbb8ce84b4098e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":null,"id":"forky","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / phx_paddock-0.4.0-py3-none-any.whl

Download URL phx_paddock-0.4.0-py3-none-any.whl
Size 38.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b3c66ac47c76a50500518faa4f90dfccc91c92217fac426fff521c87f7c43d02
BLAKE2b-256 checksum
How to use checksums
8b54eb55c70aecd30cf03785fc7a4374ed82b256b9e4b42ab67e281ad25749ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":null,"id":"forky","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

1.1.0

2 release files

1.0.0

2 release files

This release

0.4.0 This release

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page