Step-by-step Dockerfile and Docker Compose setup for containerizing Claude API Python applications in 2026. Covers secret injection, multi-stage builds, and health checks.
Containerizing a Claude API application lets you deploy it consistently across dev, staging, and production. The main challenge is injecting the API key securely without baking it into the image. This guide covers the complete production Dockerfile pattern.
my-claude-app/
├── app.py
├── requirements.txt
├── Dockerfile
├── .dockerignore
└── docker-compose.yml
anthropic>=0.50.0
fastapi>=0.115.0
uvicorn[standard]>=0.32.0
import anthropic
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from env
class AskRequest(BaseModel):
prompt: str
max_tokens: int = 512
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/ask")
def ask(req: AskRequest):
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=req.max_tokens,
messages=[{"role": "user", "content": req.prompt}]
)
return {
"text": message.content[0].text,
"input_tokens": message.usage.input_tokens,
"output_tokens": message.usage.output_tokens,
}
# ── Stage 1: build deps ──────────────────────────────────────
FROM python:3.12-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# ── Stage 2: runtime image ───────────────────────────────────
FROM python:3.12-slim
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
COPY --from=builder /install /usr/local
COPY app.py .
# Health check — pings /health without touching Claude
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
.env
.git
__pycache__
*.pyc
*.pyo
.pytest_cache
# Build
docker build -t my-claude-app .
# Run — inject key at runtime, never bake into image
docker run -p 8000:8000 -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" my-claude-app
# Test
curl -X POST http://localhost:8000/ask -H "Content-Type: application/json" -d '{"prompt": "What is prompt caching?"}'
version: "3.9"
services:
claude-app:
build: .
ports:
- "8000:8000"
env_file: .env # ANTHROPIC_API_KEY lives here
environment:
REDIS_URL: redis://redis:6379
depends_on:
redis:
condition: service_healthy
restart: unless-stopped
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
| Platform | Docker support | Secret injection | Cold start |
|---|---|---|---|
| Google Cloud Run | Native | Secret Manager --set-secrets | ~1s (min-instances=0) |
| AWS ECS / Fargate | Native | Secrets Manager task def ref | ~10s |
| Fly.io | Native | fly secrets set ANTHROPIC_API_KEY=... | ~0.5s |
| Railway | Auto-detect Dockerfile | Variables UI | ~2s |
| Vercel | No (serverless only) | Environment UI | ~200ms |
For per-model pricing to estimate container compute costs vs API costs, use the Claude API Cost Calculator. For Cloud Run deployment specifics, see the Google Cloud Run guide.