Self-Hosted Agent (Docker)

Run the whole agent — model calls, loop and tools — inside a local Docker container you own

Run the whole agent — model calls, loop and tools — inside a local Docker container you own.

Diagram

run_on=docker

🤖 Agent

🐳 Docker container

☁️ LLM API

🔧 Tools inside container

Quick Start

  1. Minimal

    One line moves the whole agent into a container.

    python
    from praisonaiagents import Agent
    
    agent = Agent(
        name="builder",
        instructions="You build things.",
        run_on="docker",
    )
    agent.start("Write a Python script that prints the first 10 primes, then run it")
  2. With a prebuilt image

    Supply your own image with praisonaiagents baked in to skip the ~1 min first-run install.

    python
    from praisonai import HostedAgent, HostedAgentConfig
    from praisonaiagents import Agent
    
    hosted = HostedAgent(
        provider="docker",
        config=HostedAgentConfig(model="gpt-4o-mini", system="Be brief."),
        image="my-org/praisonai-agent:latest",
    )
    agent = Agent(name="builder", backend=hosted)
    agent.start("Summarise the README in three bullets")
  3. Extra environment and packages

    Pass extra environment variables and packages, and disable container reuse.

    python
    from praisonai import HostedAgent, HostedAgentConfig
    from praisonaiagents import Agent
    
    hosted = HostedAgent(
        provider="docker",
        config=HostedAgentConfig(model="gpt-4o-mini"),
        env={"MY_FLAG": "1"},
        pip_packages=["praisonaiagents", "httpx"],
        keep_alive=False,
    )
    agent = Agent(name="builder", backend=hosted)
    agent.start("Fetch example.com and count the words")

How It Works

The agent on your machine ships its serialisable config into the container, which rebuilds the agent, calls the LLM and runs the tools — then returns the answer.

Diagram
Docker containerAgent (host)UserDocker containerAgent (host)Userrebuild Agent from JSONcall LLM + run toolsagent.start(request)write config + prompt as JSONone line of JSON resultresponse

The model API key travels into the container via -e KEY — the value is read from your host environment, never placed on the command line, so it never shows up in docker inspect or a process listing.


run_on="docker" vs tools_run_on="docker"

Both spellings name the same place. What differs is what crosses the boundary — the parameter name carries the scope.

tools_run_on="docker"run_on="docker"
The loop runsyour processin the container
The model is called fromyour machinethe container
Needs the API key whereyour machinethe container
Your files reachableyesonly what you mounted

The object reports which scope you chose:

python
>>> from praisonaiagents import Agent
>>> Agent(name="x", instructions="i", run_on="docker")
Agent(name='x', thinks_on='a Docker container', tools_run_on='a Docker container')

>>> Agent(name="x", instructions="i", tools_run_on="docker")
Agent(name='x', thinks_on='this machine', tools_run_on='a Docker container')

Which one do you want?

Diagram

In the container

On my machine

Where should the
model call happen?

run_on=docker
whole agent inside

tools_run_on=docker
only tools inside


Configuration Options

Pass these to HostedAgent(provider="docker", ...).

OptionTypeDefaultDescription
config`HostedAgentConfig \dict \None`NoneAgent config (instructions, llm, name, etc.). Only JSON-safe fields cross into the container.
imagestr"python:3.12-slim"Container image. Supply your own with praisonaiagents baked in to skip the first-run install.
envdict[str, str]{}Extra environment variables passed into the container.
keep_aliveboolTrueReuse one container across calls so a follow-up turn does not pay start-up again.
pip_packageslist[str]["praisonaiagents"]Packages installed inside the container on first run.

Since PR #4077 the generic backend honours image= too, so HostedAgent(provider="docker", image="my-org/agent:latest") behaves the same across the compute-backed places.

Model keys forwarded automatically

These variables are forwarded into the container when set in your host environment, passed with -e KEY (value read from your env), so they never appear in docker inspect or a process listing:

OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, GOOGLE_API_KEY, GROQ_API_KEY, MISTRAL_API_KEY, COHERE_API_KEY, OPENROUTER_API_KEY, DEEPSEEK_API_KEY, XAI_API_KEY, OPENAI_BASE_URL, OPENAI_API_BASE.


Limitations


Troubleshooting

When Docker is not running, run_on="docker" fails fast and points you at the daemon-free alternative:

text
RuntimeError: run_on='docker' cannot start: docker is not available on this machine.
  For docker, check the daemon is running (`docker info`).
  For a cloud place, check its credential is set.
  To keep the loop here and move only the tools, use tools_run_on='docker' instead.

Best Practices

Supply your own image= for production

Bake praisonaiagents into a custom image and pass it as image=. The first-run install (~1 min) disappears and start-up becomes predictable.

Leave keep_alive=True for multi-turn conversations

One container is reused across calls, so a follow-up turn does not pay start-up again. The default is already True.

Prefer tools_run_on='docker' to keep the model call local

When you want the loop on your machine — cheaper, and no API key inside the container — move only the tools with tools_run_on="docker".

Call .shutdown() when you disable keep_alive

With keep_alive=False, tear the container down with .shutdown() or use the backend as a context manager (with HostedAgent(...) as hosted:).

With keep_alive=True, the container is still cleaned up automatically

Since PR #4109, a weakref.finalize on the backend reclaims the container when the backend is collected or the process exits — so a keep_alive=True container no longer outlives your script.

.shutdown() still exists: call it when you want teardown to happen now rather than whenever GC runs.


Related