Deploying Agents Basics
10 examples to package a tiny Python agent as a Docker image, run it locally, inject secrets, and hand it to a host.
Read Choosing a Deployment Target for an Agent Workload for the placement trade-offs. These snippets focus on a stateless HTTP agent you can promote from laptop to container host.
Prerequisites
You need Python 3.10+, Docker Desktop or Engine, and an API key for a chat-completions compatible provider (OpenRouter or direct).
python -m venv .venv
source .venv/bin/activate
pip install fastapi uvicorn httpx pydanticConcept focus: Image tags, base OS packages, and platform CLI flags change. Treat paths as patterns and verify Docker/platform docs at build.
Basic Examples
1. Keep the Agent Small Enough to Deploy
A deployable first agent is a single entrypoint with env-based config and no laptop-only paths.
# agent_app.py
import os
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
MODEL = os.environ.get("AGENT_MODEL", "openai/gpt-4o-mini") # verify slug at build
API_KEY = os.environ.get("OPENROUTER_API_KEY", "")
class Turn(BaseModel):
message: str
@app.get("/healthz")
def healthz() -> dict:
return {"ok": True}
@app.post("/v1/turn")
async def turn(body: Turn) -> dict:
# Real agents call tools + loop; keep the first deploy thin
return {
"reply": f"echo: {body.message}",
"model": MODEL,
"configured": bool(API_KEY),
}- Health endpoints matter for orchestrators and load balancers
- Config comes from the environment, not hard-coded secrets
- Echo first; add model calls once the image runs cleanly
2. Pin Dependencies for Reproducible Images
# requirements.txt
fastapi==0.115.6
uvicorn[standard]==0.34.0
httpx==0.28.1
pydantic==2.10.4- Pin versions so rebuilds do not surprise you mid-incident
- Prefer a lock file (pip-tools, uv, poetry) when the team grows
- Separate dev test tools from runtime requirements when images must stay small
3. Write a Minimal Production-Shaped Dockerfile
# Dockerfile
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PORT=8080
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY agent_app.py .
# Non-root when your base image supports it cleanly
RUN useradd -m appuser && chown -R appuser /app
USER appuser
EXPOSE 8080
CMD ["uvicorn", "agent_app:app", "--host", "0.0.0.0", "--port", "8080"]- Slim bases reduce attack surface and pull time
- Copy dependency manifests before app code for better layer cache
- Bind
0.0.0.0so the process is reachable outside the container
Related: Containerizing an Agent with Docker
4. Build and Run Locally
docker build -t agent-basics:dev .
docker run --rm -p 8080:8080 \
-e OPENROUTER_API_KEY \
-e AGENT_MODEL=openai/gpt-4o-mini \
agent-basics:devcurl -s http://127.0.0.1:8080/healthz
curl -s -X POST http://127.0.0.1:8080/v1/turn \
-H 'content-type: application/json' \
-d '{"message":"hello"}'- Prove health and one turn before any cloud deploy
- Pass secrets with
-eor--env-file, never bake keys into layers - Map one host port deliberately; avoid publishing everything
5. Add a Real Model Call Behind the Same Interface
# drop-in pattern for turn()
import httpx
async def complete(message: str) -> str:
if not API_KEY:
return f"echo (no key): {message}"
async with httpx.AsyncClient(timeout=60.0) as client:
r = await client.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"model": MODEL,
"messages": [{"role": "user", "content": message}],
},
)
r.raise_for_status()
data = r.json()
return data["choices"][0]["message"]["content"]- Keep HTTP timeouts explicit; agents stall without them
- Surface provider errors as structured JSON, not raw stack traces to clients
- Log model slug and latency for later cost work
Intermediate Examples
6. Ignore Secrets and Junk in the Build Context
# .dockerignore
.venv
__pycache__
.git
.env
*.pyc
.pytest_cache
.mypy_cache.envin the context is a common secret leak into layers- Smaller context means faster builds
- Review
docker historyif you ever suspect a leaked file
Related: Environment and Secrets Management Across Deployment Targets
7. Health Checks That Match Production
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/healthz')"- Orchestrators restart based on health, not hope
start-periodabsorbs import-time cold starts- Keep
/healthzfree of model calls so probes stay cheap
8. One Compose File for Local Multi-Service Layout
# docker-compose.yml
services:
agent:
build: .
ports:
- "8080:8080"
environment:
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
AGENT_MODEL: ${AGENT_MODEL:-openai/gpt-4o-mini}
restart: unless-stoppedexport OPENROUTER_API_KEY=sk-or-...
docker compose up --build- Compose is for local parity, not always for production topology
restartpolicies preview what VPS managers do later- Externalize every secret; never commit compose overrides with keys
9. Tag Images for Promotion, Not Only latest
git_sha=$(git rev-parse --short HEAD)
docker build -t agent-basics:${git_sha} -t agent-basics:dev .
docker push your-registry.example/agent-basics:${git_sha} # when ready- Immutable tags make rollback possible
latestis fine for demos; production needs a commit or build id- Pair tags with deploy records in CI (CI/CD and Agent Lifecycle Basics)
10. Know the Next Host Path Before You Ship
| Next step | Guide |
|---|---|
| Harden the Dockerfile (multi-stage, non-root, slim) | Containerizing an Agent with Docker |
| Short HTTP agent on functions | Deploying an Agent to Vercel or AWS Lambda Serverless Functions |
| Always-on worker on a box | Long-Running Agents on a VPS: Process Management and Restarts |
| Secrets per environment | Environment and Secrets Management Across Deployment Targets |
- The image you built here is the portable unit for most container hosts
- Serverless may repackage the same code as a function handler instead of long-running uvicorn
- Decide timeouts before you copy-paste into Lambda
FAQs
Do I need Kubernetes for a first agent?
No. Local Docker plus a simple PaaS or one VPS is enough to learn deploy mechanics.
Why not put the API key in the Dockerfile?
Anyone with the image can extract it. Inject at runtime from a secret store or platform env.
Is uvicorn in the container "production ready"?
Good enough for a first service with a reverse proxy in front. Tune workers, timeouts, and process managers as load grows.
Should the agent store chat history in memory?
Not for multi-instance or restart-safe prod. Use Redis/DB early if sessions matter.
What port should I expose?
Pick one (8080 is common). Keep container port and platform PORT env aligned when hosts inject it.
Related
- Choosing a Deployment Target for an Agent Workload
- Containerizing an Agent with Docker
- Deploying an Agent to Vercel or AWS Lambda Serverless Functions
- Long-Running Agents on a VPS: Process Management and Restarts
- Environment and Secrets Management Across Deployment Targets
- Deploying Agents Best Practices
- CI/CD and Agent Lifecycle Basics
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.