Deployment Guide (Docker)
How to containerize and deploy Karo agents using Docker.
Guide: Deploying Karo Agents via Docker
This guide explains how to containerize your Karo agent application using Docker, allowing you to run it consistently locally or deploy it to any container hosting platform (like Google Cloud Run, AWS Fargate, Kubernetes, etc.).
Prerequisites
- Karo framework (
karo
) code available. - Docker installed and running (Docker Desktop or Docker Engine).
- Required environment variables available on your host machine for testing (e.g.,
OPENAI_API_KEY
,KARO_JWT_SECRET
). - Serving dependencies installed locally if you need to run
karo generate-token
(runpoetry install --with serve
inkaro
).
Overview
We provide a Dockerfile
that packages the Karo FastAPI server and its dependencies. You build this Dockerfile to create an image, which can then be run as a container. The container runs the karo serve
command internally, loading an agent configuration specified via an environment variable or volume mount. This makes your agent portable and easy to deploy.
1. Agent Configuration (YAML)
You define the agent to be served using a YAML configuration file (e.g., agent_definition.yaml
). This file specifies the agent class, components, schemas, prompt, and tools.
Important for Docker:
- Paths: Paths within this file (e.g., for ChromaDB
path
, toolfile_path
referenced in prompts or tool configs) must be valid inside the container. Use paths relative to the application root (/app
) or standard container paths like/data
. - Volume Mounting (Recommended): Instead of copying your specific
agent_definition.yaml
and data files (likeorders.csv
) into the image during build, it's highly recommended to mount them into the container at runtime using Docker volumes (-v
flag). This keeps your image generic and allows you to easily change configurations or data without rebuilding.
Example (agent_definition.yaml
- using conventional container paths):
# Sample Agent Definition for Karo Serving (Container Paths)
agent_class_path: karo.core.base_agent.BaseAgent
input_schema_path: karo.schemas.base_schemas.BaseInputSchema
output_schema_path: karo.examples.order-chatbot.main.OrderBotOutputSchema
provider_config:
type: openai
model: gpt-4o-mini
memory_config:
db_type: chromadb
path: /app/data/.karo_orderbot_db # Path inside container - mount a host volume here for persistence
collection_name: orderbot_faq
history_config:
max_history_messages: 10
prompt_config:
role_description: |
You are an Order Support Chatbot...
(Prompt text here)
...populate 'tool_parameters' with 'file_path' ('/app/data/orders.csv')... # Mount orders.csv to /app/data/orders.csv
tools:
- tool_class_path: karo.examples.order-chatbot.csv_order_reader_tool.CsvOrderReaderTool
config: {}
memory_query_results: 3
(See karo/examples/order-chatbot/agent_definition.yaml
for a full example)
2. Dockerfile and .dockerignore
A Dockerfile
and .dockerignore
file are provided in the karo
directory.
Dockerfile
: Uses a multi-stage build to install dependencies using Poetry and copies the application code into a slim Python image. It sets the defaultCMD
to runkaro serve
, using theKARO_AGENT_CONFIG_PATH
environment variable (defaulting to/app/examples/order-chatbot/agent_definition.yaml
)..dockerignore
: Excludes unnecessary files like.git
,.venv
,__pycache__
to keep the image size small and avoid copying sensitive information.
3. Building the Docker Image
Navigate to the karo
directory in your terminal and run the build command:
# In karo directory
docker build -t karo-agent-server:latest .
- Replace
karo-agent-server:latest
with your desired image name and tag (e.g.,gcr.io/my-project/karo-orderbot:v1.0
if pushing to Google Container Registry).
4. Running the Container Locally
To test the built image locally using docker run
:
# Make sure KARO_JWT_SECRET and OPENAI_API_KEY are set in your *host* environment
# or pass them explicitly with -e
# Example 1: Running with the default config baked into the image
# (Assumes the default config and its data paths are valid inside the image)
docker run -p 8000:8000 \
-e KARO_JWT_SECRET="${KARO_JWT_SECRET}" \
-e OPENAI_API_KEY="${OPENAI_API_KEY}" \
--rm --name karo_server_local \
karo-agent-server:latest
# Example 2: Mounting local config and data files (Recommended)
# Assumes you are in the karo-redone directory (parent of karo)
# 1. Create a directory for persistent DB data on host: mkdir -p ./karo_data/db
# 2. Create a directory for config/data mounts: mkdir -p ./karo_data/config
# 3. Copy your agent_definition.yaml, orders.csv, faq_data.json into ./karo_data/config/
docker run -p 8000:8000 \
-e KARO_JWT_SECRET="${KARO_JWT_SECRET}" \
-e OPENAI_API_KEY="${OPENAI_API_KEY}" \
-e KARO_AGENT_CONFIG_PATH="/app/config/agent_definition.yaml" `# Tells serve command where to find config inside container` \
# Mount your config file (read-only)
-v "$(pwd)/karo_data/config/agent_definition.yaml:/app/config/agent_definition.yaml:ro" \
# Mount your data files (read-only)
-v "$(pwd)/karo_data/config/orders.csv:/app/data/orders.csv:ro" \
-v "$(pwd)/karo_data/config/faq_data.json:/app/data/faq_data.json:ro" \
# Mount a host directory to persist the ChromaDB data
-v "$(pwd)/karo_data/db:/app/data/.karo_orderbot_db" \
--rm --name karo_server_local \
karo-agent-server:latest
-p 8000:8000
: Maps port 8000 inside the container to port 8000 on your host.-e VAR_NAME="VALUE"
: Sets environment variables inside the container. Crucially, setKARO_JWT_SECRET
and any required API keys.-v /host/path:/container/path:ro
: Mounts a file or directory from your host into the container. Use:ro
for read-only mounts (good for config/input data). Use a writable mount for data that needs to persist (like the database directory). This allows changing config/data without rebuilding.--rm
: Automatically removes the container when it stops.--name
: Assigns a name to the running container.
5. API Authentication (JWT)
The server running inside the container requires JWT authentication.
- Secret Key: Ensure the
KARO_JWT_SECRET
environment variable passed todocker run
matches the secret used to generate your tokens. - Generating Tokens: Use the CLI on your host machine (where Karo is installed):
Copy the generated token.
# Run from the project root (e.g., karo-redone) python -m karo.karo.cli.main generate-token --expires-in 1h
/invoke
)
6. Interacting with the API (Send POST
requests to http://127.0.0.1:8000/invoke
(or the appropriate host/port if changed).
Example using curl
:
curl -X POST http://127.0.0.1:8000/invoke \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
-d '{
"chat_message": "What is the status for ORD1002, email is bob@example.com?"
}'
(Replace <YOUR_JWT_TOKEN>
with the actual token)
Refer to the API Models (serving/models.py
) for the expected request (InvokeRequest
) and response (InvokeResponse
) structures.
7. Deploying to Hosting Platforms
Once you have built and tested your Docker image, you can push it to a container registry (like Google Artifact Registry, Docker Hub, AWS ECR) and deploy it to your preferred hosting platform:
- Google Cloud Run: Use
gcloud run deploy --image=<your_image_url> ...
. Set environment variables using--set-env-vars
. For configuration/data files, you might need to bake them into the image (simpler but less flexible) or use secrets/ConfigMaps mounted as volumes (more complex). Cloud Run doesn't directly support persistent host volumes like local Docker; consider using services like Cloud Storage or managed databases for persistence. - AWS Fargate / ECS / EKS / App Runner: Configure task definitions or services. Use secrets management for sensitive environment variables. Use EFS or other volume types for persistent data if needed.
- Kubernetes: Create Deployment and Service definitions. Use Kubernetes Secrets and ConfigMaps for environment/config files, and PersistentVolumes/PersistentVolumeClaims for data persistence.
- VPS / Docker: Run the
docker run
command on your server, managing environment variables securely (e.g., via systemd service files or other methods) and using host volume mounts (-v
).
The key is to provide the necessary environment variables (especially secrets) securely and handle configuration/data paths and persistence according to the chosen platform's capabilities.