Running AI agents in Docker turns fragile local setups into reproducible, production-grade deployments. Containers give you environment isolation, restart policies, resource limits, and clean networking — everything a long-running agent process needs. This guide covers the patterns used in real systems: containerizing Hermes Agent, orchestrating im-bot services, wiring up sun-port as a reverse proxy, and persisting state with PostgreSQL.
AI agents like Hermes Agent have complex runtime dependencies: Python toolchains, system libraries for browser automation, API credentials, and persistent state directories. Running them directly on a host works for development but leads to drift and breakage over time. Docker solves this with:
Dockerfile is the single source of truth for the environment--restart=unless-stopped keeps agents alive across rebootsLet's verify our Docker installation first:
$ docker --version
Docker version 28.5.1, build e180ab8
$ docker compose version
Docker Compose version v2.40.0
Choose your base image based on what the agent needs. For Hermes Agent, which depends on Python 3.10+ and system libraries for Playwright (browser automation), Debian Bookworm Slim is a solid choice:
$ docker run --rm debian:bookworm-slim cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
A practical Dockerfile for an AI agent host:
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
python3 python3-pip python3-venv \
curl git ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN python3 -m venv /opt/agent-venv
ENV PATH="/opt/agent-venv/bin:$PATH"
# Install agent runtime dependencies
RUN pip install playwright && playwright install-deps && playwright install
WORKDIR /workspace
ENTRYPOINT ["/opt/agent-venv/bin/python3"]
bookworm-slim, not alpine. Hermes Agent uses glibc-dependent Python packages (Playwright, psycopg2 for PostgreSQL) that break on musl libc. The 75 MB base image is worth avoiding hours of compatibility debugging.AI agents need to reach external APIs (LLM providers, web hooks) and often expose internal endpoints (health checks, agent dashboards). Docker's networking model gives you three main patterns:
When running sun-port (the Cloudflare Pingora-based reverse proxy) as a Docker container, host networking is the right call. It eliminates the Docker bridge NAT layer and gives the proxy direct access to the host's network interfaces:
$ docker inspect sunp --format 'HostConfig.NetworkMode: {{.HostConfig.NetworkMode}} | Status: {{.State.Status}}'
HostConfig.NetworkMode: host | Status: running
sun-port running with host networking can bind directly to ports 80 and 443 with zero overhead. The container sees the host's real IP addresses, which matters for rate limiting, client IP logging, and TLS termination.
When your agent system has multiple services (agent runtime, PostgreSQL, Redis, an API server), bridge networks with Docker Compose provide DNS-based service discovery:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
28ba1a84b409 bridge bridge local
ef21dab7d935 host host local
5a8bb62c37ed mailserver_default bridge local
6de3d2805d59 nn-os-hub-store_default bridge local
Each Compose project gets its own bridge network. Services can address each other by name — postgres resolves to the PostgreSQL container, agent-api resolves to the agent's API server. No hardcoded IPs.
Here's a production-style docker-compose.yml for an im-bot based multi-agent system with PostgreSQL persistence:
version: "3.9"
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: imbot
POSTGRES_USER: imbot
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U imbot"]
interval: 5s
retries: 5
im-bot-server:
build: ./im-bot
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: postgresql://imbot:${DB_PASSWORD}@postgres:5432/imbot
HERMES_API_KEY: ${HERMES_API_KEY}
ports:
- "127.0.0.1:3000:3000"
agent-runtime:
build:
context: .
dockerfile: Dockerfile.agent
depends_on:
- postgres
environment:
HERMES_API_KEY: ${HERMES_API_KEY}
DATABASE_URL: postgresql://imbot:${DB_PASSWORD}@postgres:5432/imbot
volumes:
- agent_workspace:/workspace
restart: unless-stopped
volumes:
pgdata:
agent_workspace:
depends_on: condition: service_healthy pattern ensures the database is accepting connections before the agent starts.Never bake API keys into Dockerfiles. Use environment variables with a .env file:
# .env (gitignored, never committed)
DB_PASSWORD=your-secure-password
HERMES_API_KEY=sk-...
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
For Git-based deployment workflows, use GitHub Actions secrets or your CI's vault. The .env file is scp'd or templated at deploy time:
$ git --version
git version 2.34.1
Push to main, pull on the server, restart with Compose:
git pull origin main
docker compose up -d --build
sun-port, built on Cloudflare's Pingora framework, handles TLS termination and routes traffic to your Dockerized agent services. A typical production setup:
# sun-port routes (simplified)
/api/agent/* → http://127.0.0.1:3000 # im-bot server
/api/chat/* → http://127.0.0.1:8000 # agent chat API
/.well-known/* → http://127.0.0.1:3000 # ACME challenges
The beauty of running sun-port in a container with host networking is that it terminates TLS once and forwards plain HTTP to your bridge-networked services — no per-container TLS overhead.
AI agents are long-running processes that can stall on API timeouts or hit rate limits. Docker's health check and restart mechanisms handle this without external monitoring:
# In docker-compose.yml
services:
agent-runtime:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9090/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 60s
restart: unless-stopped
The agent exposes a lightweight /health endpoint that checks connectivity to its LLM provider and PostgreSQL. Three consecutive failures trigger a Docker restart.
Running Hermes Agent itself in Docker is straightforward once you understand the volume mounts it needs:
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
python3 python3-pip python3-venv curl git \
&& rm -rf /var/lib/apt/lists/*
# Install Hermes Agent
RUN python3 -m venv /opt/hermes-venv && \
/opt/hermes-venv/bin/pip install hermes-agent
# Install Playwright for browser automation
RUN /opt/hermes-venv/bin/playwright install-deps && \
/opt/hermes-venv/bin/playwright install
WORKDIR /workspace
# Hermes profiles and config persist here
VOLUME ["/root/.hermes", "/workspace"]
ENTRYPOINT ["/opt/hermes-venv/bin/hermes"]
CMD ["run", "--profile", "production"]
Run it:
docker run -d \
--name hermes-agent \
--restart=unless-stopped \
-v hermes_config:/root/.hermes \
-v $(pwd)/workspace:/workspace \
-e HERMES_API_KEY=$HERMES_API_KEY \
hermes-agent:latest
Docker's logging drivers ship agent output to your existing observability stack. For im-bot and Hermes Agent, the json-file driver with log rotation is the pragmatic default:
# /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
}
}
View live agent output:
docker logs -f hermes-agent --tail=100
For production, forward to Loki, Datadog, or Elasticsearch via the appropriate Docker logging driver plugin.
Here's the end-to-end flow for deploying an AI agent stack to production:
docker compose builddocker compose push (if using a private registry)git pull && docker compose pulldocker compose run --rm agent-runtime npx prisma migrate deploydocker compose up -ddocker compose ps — all services should show "healthy"docker compose logs --tail=50 agent-runtimecurl -f https://agent-rule.com/api/healthDocker turns AI agent deployment from a fragile art into a reliable engineering practice. The patterns above are battle-tested in production systems running im-bot, Hermes Agent, and sun-port — every command verified on real hardware.