Containerizing an ADK agent
Copy as MarkdownSo far we’ve run the trading agent two ways: locally with adk run, and deployed to Agent Platform runtime with a little Python script that packages the code and hosts it for us. Both are handy, but in each case something else is deciding how your agent gets hosted. Sometimes you just want a plain container — one image you can run on your laptop today, and on Cloud Run, GKE, or Agent Platform runtime (in bring-your-own-container mode) tomorrow. Same artifact, runs anywhere Docker does.
This post kicks off the containerizing section: we’ll wrap the agent up as a container and run it locally. (As always with this agent, these are paper trades — but they do execute, so don’t point it at a funded account.)
The one shift in mindset is how we serve it. Instead of adk run, which gives you an interactive CLI, we’ll use adk api_server to expose the agent as an HTTP API on port 8080. That’s the thing we put in a container.
The Dockerfile
Nothing exotic here — a slim Python base, install the requirements, copy in the agent, and set the start command:
FROM python:3.12-slim
WORKDIR /app
COPY trading_agent/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN mkdir -p trading_agent
COPY trading_agent/agent.py trading_agent/
CMD ["sh", "-c", "adk api_server --host 0.0.0.0 --port ${PORT:-8080} ${SESSION_SERVICE_URI:+--session_service_uri $SESSION_SERVICE_URI} ${MEMORY_SERVICE_URI:+--memory_service_uri $MEMORY_SERVICE_URI} --no-reload /app"]
The CMD is the only part worth slowing down on. ${PORT:-8080} defaults the port to 8080, but honors $PORT if the platform sets one (Cloud Run injects it, for example). The two ${SESSION_SERVICE_URI:+…} expansions are the neat bit: that syntax only adds the flag when the variable is set. So the very same image runs two ways — with nothing set it’s a self-contained agent with in-memory sessions, and set those two URIs and it wires straight into a managed Sessions and Memory backend instead. One image, both modes.
Building and running locally
Drop your keys into a docker-env file. These get passed in at run time with --env-file, so they never get baked into the image:
# Alpaca API keys
APCA_API_KEY_ID=your-key-id
APCA_API_SECRET_KEY=your-key
# Google AI Studio key for the ADK agent
GOOGLE_API_KEY=your-key
Then build and run — that’s the two lines in docker.sh:
docker build . -t trading_agent
docker run -it --rm --env-file docker-env -p 8080:8080 trading_agent
Since we didn’t set SESSION_SERVICE_URI or MEMORY_SERVICE_URI, this is the self-contained mode: the agent serves on http://localhost:8080 with in-memory sessions, so its memory resets when the container stops. That’s fine for checking everything works.
Trying it out
adk api_server is an API, not a chat prompt, so to exercise it we create a session and post a message to the /run endpoint. There’s a small trade-docker.sh that does exactly that against localhost:8080, then pulls the agent’s final reply out of the response:
With the container running in one terminal, run it from another:
./trade-docker.sh
It creates a session, kicks off the cycle, and prints the agent’s final reply — the same end-of-cycle report we saw back when we ran it with adk run, just served over HTTP this time.
Tip: the script takes an optional message, so ./trade-docker.sh "go" works too if you’d rather not type the default.
Connecting to a memory backend
The local container forgets everything when it stops. To give it persistent Sessions and Memory instead, we flip on the two URIs from the CMD — and that’s what docker-env-remote and docker-remote.sh are for.
The remote env file points at Vertex AI and a managed backend resource:
# Alpaca API keys
APCA_API_KEY_ID=your-key-id
APCA_API_SECRET_KEY=your-key
GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT=your-project
GOOGLE_CLOUD_LOCATION=us-west1
SESSION_SERVICE_URI=agentengine://projects/your-project/locations/us-west1/reasoningEngines/RESOURCE_ID
MEMORY_SERVICE_URI=agentengine://projects/your-project/locations/us-west1/reasoningEngines/RESOURCE_ID
Warning: on Vertex AI you authenticate with your Google Cloud identity, not an API key. Set GOOGLE_GENAI_USE_VERTEXAI=TRUE and make sure there’s no GOOGLE_API_KEY or GEMINI_API_KEY in this file. It’s the same point as the managed deploy: the API key is a long-lived dev token, and in this mode we use your real identity instead.
To get that identity into the container, docker-remote.sh mounts your local gcloud config read-only:
docker build . -t trading_agent
docker run -it --rm \
--env-file docker-env-remote \
-v ~/.config/gcloud:/root/.config/gcloud:ro \
-p 8080:8080 trading_agent
That -v ~/.config/gcloud:…:ro is the bit that lets the containerized agent call Google Cloud as you, without baking any credentials into the image — handy for local testing. (In production you’d give the container its own service account instead.)
The one missing piece is the resource those agentengine:// URIs point at. That’s an Agent Platform resource (currently called an “agent engine”), and creating one is exactly what we do next.
What’s next
We’ve now got a single container image that runs fully self-contained on your laptop, and — with a couple of env vars — talks to a managed memory backend instead. That portability is the whole point: this same image is what we’ll push to Cloud Run, GKE, and Agent Platform runtime later in this section.
First, though, we need that backend resource. Next up: creating an Agent Platform resource.