Containerizing an Agent with Docker
A Docker image freezes the agent runtime, OS packages, and Python deps so every host runs the same binary surface.
Search across all documentation pages
A Docker image freezes the agent runtime, OS packages, and Python deps so every host runs the same binary surface.
Use this recipe when you promote a working agent from a laptop venv into something a registry and orchestrator can ship.
Build a multi-stage, non-root image with pinned deps, a health endpoint, runtime-injected secrets, and tags you can roll back.
.dockerignore that excludes .venv, .git, .env, and tests caches.ca-certificates; add browsers only when required).PORT from the environment.git SHA); never rely on latest alone in production.-e, orchestrator secrets, or a secrets sidecar) - never ENV KEY=... with real values in the Dockerfile.healthz + one agent turn against the running container before cutting traffic.# requirements.txt (pin in real repos)
fastapi==0.115.6
uvicorn[standard]==0.34.0
httpx==0.28.1
pydantic==2.10.4# agent_app.py
import os
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Turn(BaseModel):
message: str
@app.get("/healthz")
def healthz() -> dict:
return {"status": "ok"}
@app.post("/v1/turn")
async def turn(body: Turn) -> dict:
# Replace with your agent loop; keep I/O timeouts explicit
return {"reply": body.message[::-1], "model": os.getenv("AGENT_MODEL", "unset")}# Dockerfile
FROM python:3.12-slim AS builder
WORKDIR /build
RUN apt-get update && apt-get install -y --no-install-recommends build-essential \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --upgrade pip \
&& pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
FROM python:3.12-slim AS runtime
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PORT=8080
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --create-home --uid 10001 appuser
COPY --from=builder /wheels /wheels
COPY requirements.txt .
RUN pip install --no-cache-dir --no-index --find-links=/wheels -r requirements.txt \
&& rm -rf /wheels
COPY agent_app.py .
USER appuser
EXPOSE 8080
CMD ["sh", "-c", "uvicorn agent_app:app --host 0.0.0.0 --port ${PORT}"]docker build -t your-registry.example/agent:$(git rev-parse --short HEAD) .
docker run --rm -p 8080:8080 \
-e PORT=8080 \
-e AGENT_MODEL=openai/gpt-4o-mini \
-e OPENROUTER_API_KEY \
your-registry.example/agent:$(git rev-parse --short HEAD)| Include | Exclude |
|---|---|
| Language runtime and pinned libs | .env and secret files |
| Minimal OS libs tools need | Dev compilers in the final stage |
| App source and static prompts if versioned with code | Local datasets you mount at runtime |
| Non-root user definition | SSH keys, cloud credentials |
Prompts and tool schemas that change with releases should be versioned with the image or loaded from an explicit config version (Versioning Prompts and Tool Schemas Alongside Code).
pip install --no-cache-dir (or equivalent) to avoid leftover indexes in layers.Agents that run Playwright, OCR, or native libs need extra packages and often larger images.
Keep those deps in a dedicated image flavor (agent:tools) rather than bloating every API replica.
Sandbox code-exec tools in separate containers when possible (Containerized Sandboxes for Code-Executing Agent Tools).
Most hosts expect:
PORT or a documented portSIGTERM so in-flight turns can finish or checkpoint# pattern: fail fast on missing prod secrets
if os.getenv("ENV") == "production" and not os.getenv("OPENROUTER_API_KEY"):
raise SystemExit("OPENROUTER_API_KEY required in production")ENV or COPY .env. Image history and registries leak them./tmp is large and durable. Many platforms give small ephemeral disks.latest only tags. You cannot roll back cleanly..dockerignore slows CI and can include secrets.| Approach | When | Trade-off |
|---|---|---|
| Single-stage Dockerfile | Tiny demos | Larger, dirtier images |
| Distroless / chainguard-style bases | Hardened prod | Harder debugging shells |
| Buildpacks | Teams avoiding Dockerfiles | Less control over OS packages |
| Zip deploy (Lambda) without container | Pure serverless Python | Different packaging rules |
| Nix / deterministic builds | Strong supply-chain goals | Steeper learning curve |
Not required. Compose is excellent for local multi-service setups. Production often uses a managed orchestrator or a single container service.
Mount Secrets as env vars or files; reference them from the Deployment. Rotate by updating the Secret and rolling pods.
If they ship with the release, yes or load a versioned object from storage. Avoid editing prompts only on a live server with no audit trail.
Official language slim images are a solid default. Pin digests for higher supply-chain control when your org requires it.
Keep a debug image flavor with a shell for incidents, or use ephemeral debug sidecars your platform supports.
Sometimes via Lambda container images, with an adapter entrypoint. Prefer one primary runtime path unless you invest in dual smoke tests.
In a dedicated tools image or a managed browser service - not in every lightweight API container.
Small enough for fast pulls on scale-out. Measure pull time under deploy, not vanity MB counts alone.
Related: Deploying Agents Basics
Related: Choosing a Deployment Target for an Agent Workload
Related: Deploying an Agent to Vercel or AWS Lambda Serverless Functions
Related: Environment and Secrets Management Across Deployment Targets
Related: Deploying Agents Best Practices
Related: Containerized Sandboxes for Code-Executing Agent Tools
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.
Reviewed by Chris St. John·Last updated Jul 16, 2026