Deploying a Google ADK Agent with Docker and Docker Compose
π This blog post is based on the Medium article: Deploying a Google ADK Agent with Docker and Docker Compose by Rohan Mitra
In this post, we'll walk through building, running, and deploying a Google ADK (Agent Development Kit) application, first in Docker, and then with Docker Compose for multi-service setups.
We'll use a medical appointmentβbooking chatbot as our example. The chatbot collects user information and makes a mock tool call to book the appointment. All the code for this tutorial can be found on my Github repo
What You'll Learn:
- How to build an ADK agent from scratch
- How to expose it as an API service
- How to run it inside Docker
- How to connect it to other services (like FastAPI) with Docker Compose
π Part 0: Building the ADK Agent
Before deploying, we'll build the agent in Python.
π‘ Tip: Make sure your Google Cloud project is active and billing is enabled, or the ADK will fail to authenticate.
Directory Structure
med-agent/
βββ __init__.py
βββ agent.py
βββ tools.py`agent.py`
from google.adk.agents import Agent
from .tools import make_appointment
BASE_PROMPT = """
You are a medical appointmentβbooking assistant... [full prompt on Github]
"""
root_agent = Agent(
name="doctor_appointment_agent",
model="gemini-2.5-pro",
description="Medical appointmentβbooking assistant",
instruction=BASE_PROMPT,
tools=[make_appointment],
)`tools.py`
from typing import Any, Dict, List, Optional
import uuid
def make_appointment(...):
"""Tool to book a doctor's appointment."""
# Full function on Github
print("[make_appointment] Received appointment request:")
... #Can add logic to check available slots and
# make call to Google Calendar API to book
confirmation_id = str(uuid.uuid4())
return {
"success": True,
"confirmation_id": confirmation_id,
"message": "Appointment booked.",
"received": received,
}Environment Setup
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_PROJECT=<project-name>
export GOOGLE_CLOUD_LOCATION=<region>Authenticate:
gcloud init
gcloud auth application-default loginTest locally:
adk webRunning adk web will open a web UI to allow you to chat with your agent. This is great for testing the app during development phases, but deploying the application requires creating a server.
π³ Part 1: Running ADK Agent in Docker
Instead of running the web UI, we'll use the API server. We use the adk api_server command instead of adk web to expose the ADK app as a server.
We begin by creating a Dockerfile that will use adk api_server to host the app. Create a Dockerfile at the same directory level as the med-agent folder and include the following:
Dockerfile
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y build-essential curl && \
rm -rf /var/lib/apt/lists/*
ENV PYTHONUNBUFFERED=1 \
GOOGLE_GENAI_USE_VERTEXAI=TRUE \
GOOGLE_CLOUD_PROJECT=<your-project> \
GOOGLE_CLOUD_LOCATION=<region>
RUN pip install --no-cache-dir \
google-adk \
google-genai==1.27.0 \
python-dotenv \
requests
COPY . .
EXPOSE 80
CMD ["adk", "api_server", "--host", "0.0.0.0", "--port", "80", "./med-agent"]Note how we expose the ADK server on port 80 for host 0.0.0.0 . Since the ADK app requires the gcloud credentials, these must be supplied at runtime. These can also be included in the container (in part 2 below), but is included externally for this example to show both possibilities.
To build & run the docker container, use the following commands:
Build & Run
docker build -t med-agent:latest .
docker run --rm -p 8000:80 \
-v ~/.config/gcloud/application_default_credentials.json:/app/gcloud_credentials.json:ro \
-e GOOGLE_APPLICATION_CREDENTIALS=/app/gcloud_credentials.json \
med-agent:latestYour ADK server is now available at http://localhost:8000 . Hence, requests to the ADK server can be sent to port 8000 which docker will map to port 80 inside the container.
π Callout: Common Docker Mistakes
Wrong build context: Make sure you run docker build from the folder above med-agent/
Missing credentials: Without mounting your Google Cloud credentials, the API will fail at runtime.
Script to Create a Session & Send requests:
To interact with the API server, the client needs to first create a session with the server. Once the session is created using the userid and sessionid, subsequent requests to interact with the ADK agent must use the same userid and sessionid keys in the payload.
Creating a session [mandatory before first message]:
user_id = "user12345"
session_id = "session12345"
ADK_API_BASE_URL="http://localhost:8000"
response = requests.post(
f"{ADK_API_BASE_URL}/apps/med-agent/users/{user_id}/sessions/{session_id}",
headers={"Content-Type": "application/json"},
data=json.dumps({})
)
print(response.json())Sending a message [only after session is created]:
adk_payload = {
"app_name": "med-agent", #Same as the name of the folder of the agent
"user_id": user_id,
"session_id": session_id,
"new_message": {"role": "user", "parts": [{"text": "Hello"}]},
}
ADK_API_BASE_URL="http://localhost:8000"
response = requests.post(
f"{ADK_API_BASE_URL}/run",
headers={"Content-Type": "application/json"},
data=json.dumps(adk_payload),
)
print(response.json())The ADK app has now been successfully deployed to Docker!
Here is the current directory structure for reference:
.
βββ client.ipynb
βββ Dockerfile
βββ med-agent
βββ __init__.py
βββ agent.py
βββ tools.pyπ Part 2: Using Docker Compose with FastAPI
Let's connect our agent to a FastAPI app so it can handle requests from another service.
Assume a dummy FastAPI app that needs to make requests to the ADK agent. This app may use the ADK agent to perform some tasks (like perform and summarize results of a google search), or may be an interface between ADK and some other chat platform.
We create a dummy FastAPI app in app.py
Dummy FastAPI app used in this tutorial (full code on [Github](http://github.com/ro1406/adk-tutorials)):
ADK_API_BASE_URL = os.getenv("ADK_AGENT_URL")
if not ADK_API_BASE_URL:
ADK_API_BASE_URL = "http://localhost:8000"
def ensure_session_exists(user_id: str, session_id: str) -> bool:
session_url = f"{ADK_API_BASE_URL}/apps/med-agent/users/{user_id}/sessions/{session_id}"
#Uses logic like in Part 1 to create the session for the current user
try:
response = requests.post(
session_url,
headers={"Content-Type": "application/json"},
data=json.dumps({}),
)
return True
except requests.exceptions.RequestException as e:
print("Failed to ensure session exists for user %s: %s", user_id, e)
return False
#Main chat endpoint
@app.post("/chat", response_model=ChatResponse)
async def chat_endpoint(chat_request: ChatRequest):
user_message = chat_request.message
user_id = chat_request.user_id
session_id = chat_request.session_id
# Any additional logic on message storing/preprocessing etc goes here
# Check if session exists, if not, create it
if ensure_session_exists(user_id, session_id):
print("Session exists/has been created")
else:
raise Exception("Session does not exist and couldnt create it")
# Use the session_id and user_id to send message to the agent
# Assumes only text messages supported for now
adk_payload = {
"app_name": "med-agent",
"user_id": user_id,
"session_id": session_id,
"new_message": {"role": "user", "parts": [{"text": user_message}]},
}
#Send the message to the ADK agent using /run endpoint
response = requests.post(
f"{ADK_API_BASE_URL}/run",
headers={"Content-Type": "application/json"},
data=json.dumps(adk_payload),
)
if response.status_code != 200:
print(f"Error: {response.text}")
agent_response = parse_agent_response(response)
return ChatResponse(response=agent_response['content'])We need a docker compose to build a container that contains both the FastAPI app and the ADK app it needs to communicate with. Hence, both services need to be deployed using docker compose.
Since we need to include FastAPI and Pydantic libraries, we can add that to our current Dockerfile by updating the RUN command as follows:
RUN pip install --no-cache-dir \
google-adk \
google-genai==1.27.0 \
python-dotenv \
requests \
fastapi[standard] \
pydanticNow, we create a docker_compose.yml in the main directory (same level as Dockerfile), and include both services as follows:
services:
server:
build: .
command: ["fastapi", "dev", "app.py", "--host", "0.0.0.0", "--port", "8000"]
depends_on:
- agent
ports:
- "8000:8000"
environment:
- ADK_AGENT_URL=http://agent:8002
networks:
- med-agent-backend
agent:
build: .
command: ["adk", "api_server", "--host", "0.0.0.0", "--port", "8002", "./med-agent"]
ports:
- "8002:8002"
volumes:
- ~/.config/gcloud/application_default_credentials.json:/app/gcloud_credentials.json
environment:
- GOOGLE_APPLICATION_CREDENTIALS=/app/gcloud_credentials.json
- GOOGLE_GENAI_USE_VERTEXAI=TRUE
- GOOGLE_CLOUD_PROJECT=<project-name>
- GOOGLE_CLOUD_LOCATION=<region>
networks:
- med-agent-backend
networks:
med-agent-backend:
driver: bridgeThe docker compose exposes port 8000 for the main FastAPI app, and uses a different port for the ADK API server (port 8002 in this example). It also includes the necessary env variables and the Google Cloud credentials.
π Tip: Why Different Ports?
Keeping FastAPI and the ADK agent on separate ports (8000 and 8002) ensures they can run independently but still communicate inside the Docker network.
Note the directory structure is such that the med-agent folder exists at the same level as the docker_compose.yml which is why the build context is ./ for the agent service.
Now you can build & run the whole container with both services using:
docker compose -f docker_compose.yml upOnce the server is ready for requests, you can use this code snippet to test the server:
import requests
import json
from pprint import pprint
response = requests.post("http://localhost:8000/chat", json={
'user_id':'user123',
'session_id':'session123',
"message": "Hello, I want to book an appointment with an eye doctor"})
pprint(response.json())Final directory structure:
.
βββ app.py
βββ client.ipynb
βββ docker_compose.yml
βββ Dockerfile
βββ med-agent
βββ __init__.py
βββ agent.py
βββ tools.pyThat's the end of the tutorial! You can find the full code along with a client.ipynb with code on how to invoke the endpoint on my Github repo.
All the code used in this tutorial can be found at: github.com/ro1406/adk-tutorials
You can connect with me on LinkedIn at: https://www.linkedin.com/in/rohan-mitra14/


