Skip to main content

axio-tools-docker

PyPI Python License: MIT

Docker sandbox tools for axio.

DockerSandbox is an async context manager that spins up an isolated Docker container on entry and removes it on exit. Inside the context it exposes six axio tools - the same shell, write_file, read_file, list_files, run_python, and patch_file as axio-tools-local, but every operation runs inside the container, never on the host.

Requirements

Docker must be installed and running:

docker info   # should succeed

The package talks to the Docker Engine API directly via aiodocker - the docker CLI is not required.

Installation

pip install axio-tools-docker

Quick start

import asyncio
from axio.agent import Agent
from axio.context import MemoryContextStore
from axio.testing import StubTransport, make_text_response
from axio_tools_docker import DockerSandbox

async def main() -> None:
    transport = StubTransport([make_text_response("Done.")])
    async with DockerSandbox(image="python:3.12-alpine") as sandbox:
        agent = Agent(
            system="You are a coding assistant. Use the sandbox tools to run code safely.",
            tools=sandbox.tools,
            transport=transport,
        )
        ctx = MemoryContextStore()
        result = await agent.run("Print hello from Python.", ctx)
        print(result)

asyncio.run(main())

Sandbox tools

These mirror axio-tools-local exactly - same names and field schemas:

Tool Description
shell Run a shell command; returns stdout + stderr. Supports timeout, cwd, stdin.
write_file Create or overwrite a file. Parent directories are created automatically.
read_file Read a file with optional start_line/end_line, line_numbers, max_chars.
list_files List a directory; directories first with a trailing /.
run_python Execute a Python snippet in a subprocess. Supports timeout, cwd, stdin.
patch_file Replace lines from_line..to_line (1-indexed, inclusive). to_line = from_line - 1 inserts.

Container lifecycle

The container is created on __aenter__ and removed on __aexit__ (docker rm -f). Cleanup runs even when the body raises an exception:

from axio_tools_docker import DockerSandbox
from axio import Agent

async def run(ctx):
    agent = Agent(
        system="You are a coding assistant. Use the sandbox tools to run code safely.",
        tools=ctx.sandbox.tools,
    )

    async with DockerSandbox(image="alpine:latest") as sandbox:
        await agent.run("...", ctx)

    # container removed here (unless remove=False)

The image is pulled automatically if not present locally. If the Docker daemon is unreachable, __aenter__ raises immediately:

RuntimeError: Docker daemon not available at 'unix:///var/run/docker.sock': ...

The running container's ID is available as sandbox.container_id inside the async with block.

Named containers and reuse

Pass name= to give the container a fixed name. If a container with that name already exists, the sandbox starts it if needed and attaches instead of creating a new one. Attached containers are never removed on exit - regardless of remove:

import asyncio
from axio_tools_docker import DockerSandbox

async def first_session() -> None:
    async with DockerSandbox(image="python:3.12-slim", name="my-sandbox", remove=False) as sb:
        await sb.exec("pip install requests")

async def second_session() -> None:
    async with DockerSandbox(name="my-sandbox") as sb:
        result = await sb.exec("python3 -c 'import requests; print(requests.__version__)'")

asyncio.run(first_session())
asyncio.run(second_session())

Named volumes

Named volumes are managed by the Docker daemon and persist across container restarts. Use them to share state between sandbox sessions:

import asyncio
from axio_tools_docker import DockerSandbox

async def main() -> None:
    async with DockerSandbox(
        image="python:3.12-alpine",
        named_volumes={"/data": "my-project-data"},
    ) as sb:
        await sb.write_file("/data/state.json", '{"count": 1}')
    # Container removed, volume survives.

    async with DockerSandbox(
        image="python:3.12-alpine",
        named_volumes={"/data": "my-project-data"},
        volumes_remove=True,   # delete the volume on exit
    ) as sb:
        raw = await sb.read_file_bytes("/data/state.json")
        assert raw.decode() == '{"count": 1}'

asyncio.run(main())

Docker creates the volume automatically if it does not exist yet.

Configuration

import os
from axio_tools_docker import DockerSandbox

sandbox = DockerSandbox(
    os.getenv("DOCKER_HOST", "unix:///var/run/docker.sock"),  # Docker daemon URL, optional
    image="python:3.12-slim",
    memory="512m",  # memory limit: "256m", "1g", …
    cpus="2.0",  # CPU limit
    network=False,  # False=none, True=default, str=explicit mode
    workdir="/workspace",
    volumes={"/workspace": "/tmp/host-dir"},  # {container_path: host_path}
    named_volumes={"/data": "my-project-data"},  # named Docker volumes
    volumes_remove=False,  # remove named volumes on exit
    env={"PYTHONPATH": "/app"},
    user="nobody",
    name="my-agent-sandbox",
    remove=False,
    read_only=True,  # read-only root filesystem
    shm_size="64m",  # /dev/shm size
    cap_add=["NET_ADMIN"],  # add Linux capabilities
    cap_drop=["ALL"],  # drop Linux capabilities
    privileged=False,
    ulimits={"nofile": (1024, 65536), "nproc": 512},
    tmpfs={"/tmp": "size=128m,mode=1777"},
    ports={8080: 8080},  # {container_port: host_port}
    platform="linux/amd64",
    extra_hosts={"host.docker.internal": "host-gateway"},
    devices=["/dev/net/tun", "/dev/sda:/dev/xvda:r"],
    dns=["8.8.8.8", "1.1.1.1"],
)

assert sandbox.image == "python:3.12-slim"
assert sandbox.memory == "512m"
assert sandbox.cpus == "2.0"
assert sandbox.network == False
Parameter Type Default Description
url str "unix:///var/run/docker.sock" Docker daemon URL (unix socket or TCP). Positional.
image str "python:latest" Container image. Pulled automatically if not present.
memory str "256m" Memory limit. Accepts k/m/g suffixes.
cpus str "1.0" CPU limit as a decimal string.
network bool | str False Network mode. False → none. True → Docker default. String → explicit NetworkMode (e.g. "host", "bridge", "my-net").
workdir str "/workspace" Working directory inside the container.
volumes dict[str, str] {} Bind mounts as {container_path: host_path}.
named_volumes dict[str, str] {} Named Docker volumes as {container_path: volume_name}. Created automatically if absent.
volumes_remove bool False Remove named volumes on exit. No effect when attached to an existing container.
env dict[str, str] {} Environment variables passed to all commands.
user str "" User to run as (e.g. "nobody", "1000").
name str "" Container name. Attaches to existing container if found; creates new one otherwise.
remove bool True Remove container on exit. No effect when attached to an existing container.
read_only bool False Read-only root filesystem. Combine with tmpfs for writable scratch paths.
shm_size str "" /dev/shm size (e.g. "64m"). Useful for PyTorch / shared-memory IPC.
cap_add list[str] [] Linux capabilities to add (e.g. ["NET_ADMIN", "SYS_PTRACE"]).
cap_drop list[str] [] Linux capabilities to drop (e.g. ["ALL"]).
privileged bool False Extended privileges - full capability set and device access. Use with care.
ulimits dict[str, int | tuple[int, int]] {} Resource limits. {"nofile": 1024} → soft=hard=1024. {"nofile": (1024, 65536)} → soft/hard split.
tmpfs dict[str, str] {} Tmpfs mounts as {path: options} (e.g. {"/tmp": "size=128m,mode=1777"}).
ports dict[int, int] {} Port bindings as {container_port: host_port}. Only meaningful when network != False.
platform str "" Platform override (e.g. "linux/amd64", "linux/arm64").
extra_hosts dict[str, str] {} Extra /etc/hosts entries as {hostname: ip}.
devices list[str] [] Host devices to expose. Format: "/dev/sda", "/dev/sda:/dev/xvda", "/dev/sda:/dev/xvda:r".
dns list[str] [] DNS servers (e.g. ["8.8.8.8"]). Only meaningful when network != False.

Part of the axio ecosystem

axio · axio-tools-local · axio-tools-mcp · axio-tui

License

MIT

Metadata

Release files for axio-tools-docker 0.11.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 axio-tools-docker 0.11.0
File Size Uploaded
axio_tools_docker-0.11.0.tar.gz 50.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for axio-tools-docker 0.11.0
File Interpreter ABI Platform
axio_tools_docker-0.11.0-py3-none-any.whl Python 3 none any Details

Total release size: 64.3 kB

Release files / axio_tools_docker-0.11.0.tar.gz

Download URL axio_tools_docker-0.11.0.tar.gz
Size 50.8 kB
Tags Source
SHA-256 checksum
How to use checksums
64b2e4c85fa01fe2a3fba08a053e592a79997db93d7a915731415f24d4a07039
BLAKE2b-256 checksum
How to use checksums
bb453c6e01bd4f34124135f3f4f3b0817f691df4e360e24112b7aec8e2975374
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.

Transparency log

Release files / axio_tools_docker-0.11.0-py3-none-any.whl

Download URL axio_tools_docker-0.11.0-py3-none-any.whl
Size 13.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
da55c969ca39b24dab50c25f1fe8835de0b517e3abe9bee8dbac1d2233b5bb5a
BLAKE2b-256 checksum
How to use checksums
a0bba99c4aa58a5fb554e7c689ffb20b25e0a70299b5a237df6e5107972cd036
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.

Transparency log

Release history Release notifications | RSS feed

0.11.3

2 release files

0.11.2

2 release files

0.11.1

2 release files

This release

0.11.0 This release

2 release files

0.10.1

2 release files

0.10.0

2 release files

0.9.9

2 release files

0.9.8

2 release files

0.9.7

2 release files

0.9.6

2 release files

0.9.5

2 release files

0.9.4

2 release files

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

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