Start the complete Agent Barn development environment with Docker. When you finish, you will have the web app, Product API, Ingest API, Communications service, Domain Event worker, LiteLLM and its PostgreSQL database, the application PostgreSQL and Redis services, a local k3d Kubernetes cluster, the Hermes and OpenClaw Runtime images, and Runtime Kubernetes resources for any Agent you start.
Application services run in Docker Compose. Agent Runtime resources run inside the local k3d cluster. The Communications service is required for Communication Connections and provider delivery, and Product, Ingest, and Communications are separate HTTP boundaries rather than one API.
What you will accomplish
By the end of this guide, you will be able to:
- Open Agent Barn at
http://localhost:3000 - Sign in as the bootstrap Platform Administrator
- Create an Organization
- Hire and start a headless Agent inside the local Kubernetes cluster
- Add a Communication Connection through the local Communications service
- Route model requests through the local LiteLLM proxy
- See Conversation Messages and Tool Calls arrive through their separate paths
- Stop and resume the environment without losing local database data
Before you begin
Required software
You need only:
- Git
- Docker with Linux containers
- A running Docker daemon
- Sufficient local CPU, memory, and disk capacity
- OpenSSL, used below to generate local keys
Run the commands in this guide from a Bash-compatible terminal. On Windows, use WSL2 with Docker Desktop integration enabled. If the openssl command is unavailable, install OpenSSL using your operating system's package manager before continuing.
Docker Desktop is the simplest option on macOS and Windows. On Windows, run the repository through WSL2 with Docker Desktop's Linux-container backend.
The complete Docker workflow does not require host installations of Python, Node.js, uv, pnpm, k3d, or Helm. The repository supplies a k3d-runner container for the k3d command. Native tools are needed only for host-managed development, testing, or selected maintenance workflows.
Required accounts and credentials
You need:
- An OpenRouter API key for the models your Agents will use.
Both Agent Barn and aai-cli are public. You do not need membership in AAI Labs or permission to access a private repository.
You need an OpenRouter API key for the configured model-routing path. The runtime Dockerfiles clone the public aai-labs/aai-cli repository directly; the local build does not require a GitHub personal access token. An optional Agent GitHub tool Integration can separately use credentials.
Local ports
Make sure these default host ports are available before starting the stack:
| Port | Service |
|---|---|
3000 | Agent Barn web app |
5432 | Application PostgreSQL database |
6379 | Redis |
7070 | LiteLLM |
8000 | Product API |
8001 | Ingest API |
8002 | Communications service |
16443 | Local k3d Kubernetes API |
These are local development ports. They are not instructions to expose these services to the internet.
If a port is already in use, check the corresponding settings in .env.spec and the local cluster configuration before starting. Keep any changed port consistent with the addresses used later in this guide.
Clone the repository
git clone https://github.com/aai-labs/agent-barn.git
cd agent-barnThe GitHub repository uses the current agent-barn name. The local Kubernetes namespaces intentionally retain the historical agent-farm naming.
Create the local configuration
./run.sh expects a .env file in the repository root. You do not have to create it by hand:
./run.shIf .env is missing, ./run.sh creates it from .env.spec, reports the values you still need to supply, and exits. Replace the empty or placeholder values, then run ./run.sh again.
.env.spec remains the authoritative inventory and description of every supported local variable. Read it when you need to know what a setting does.
Required values
The stack does not start until these values are set:
| Variable | Purpose |
|---|---|
POSTGRES_USER | Application database user |
POSTGRES_PASSWORD | Application database password |
POSTGRES_DB | Application database name |
POSTGRES_PORT | Published database port |
SECRET_SIGNING_KEY | Signs application tokens |
PLATFORM_ADMIN_CREDENTIALS | Bootstrap Platform Administrator sign-in |
ENVIRONMENT | Environment name for the local run |
UI_APP_URL | Public URL of the web app |
API_PORT | Published Product API port |
AGENT_TOKEN_ENCRYPTION_KEY | Encrypts stored credentials |
OPENROUTER_API_KEY | Upstream model access for LiteLLM |
LITELLM_MASTER_KEY | Protects LiteLLM virtual keys |
OPENCLAW_IMAGE | Pinned OpenClaw Runtime image reference |
HERMES_IMAGE | Pinned Hermes Runtime image reference |
Database and application
POSTGRES_USER=agentbarn
POSTGRES_PASSWORD=<local-database-password>
POSTGRES_DB=agentbarn
POSTGRES_PORT=5432
API_PORT=8000
ENVIRONMENT=local
UI_APP_URL=http://localhost:3000
SECRET_SIGNING_KEY=<random-signing-key>
PLATFORM_ADMIN_CREDENTIALS=admin@example.com:<secure-password>The Platform Administrator password must contain:
- At least eight characters
- An uppercase letter
- A lowercase letter
- A digit
Generate a random signing key with:
openssl rand -hex 32Do not use the generated signing key as the Platform Administrator password.
Credential encryption
Agent Barn uses an encryption key to protect stored credentials.
From your terminal, generate a new key:
openssl rand -base64 32 | tr '+/' '-_'Copy the generated value into the AGENT_TOKEN_ENCRYPTION_KEY entry in the .env file at the repository root:
AGENT_TOKEN_ENCRYPTION_KEY=PASTE_THE_GENERATED_VALUE_HEREReplace PASTE_THE_GENERATED_VALUE_HERE with the complete output from the command. Do not enter the command itself as the value.
Generate this key once for your local installation and keep it stable across restarts. Replacing it later prevents Agent Barn from reading credentials encrypted with the previous key.
Keep .env private and do not commit it to Git.
Model routing
OPENROUTER_API_KEY=<openrouter-api-key>
LITELLM_MASTER_KEY=<stable-litellm-master-key>Generate a local LiteLLM master key with:
printf 'sk-%s\n' "$(openssl rand -hex 16)"Set the generated value once and keep it stable. LiteLLM uses it to protect the virtual keys it stores. Changing it between runs breaks Agents created with keys protected by the previous value.
Agent Runtime images
Read the versions pinned by the repository:
printf 'OpenClaw: '
cat openclaw-base/VERSION
printf 'Hermes: '
cat hermes-base/VERSIONSet both image references using those exact tags:
OPENCLAW_IMAGE=agentbarn-openclaw-base:<openclaw-version>
HERMES_IMAGE=agentbarn-hermes-base:<hermes-version>The image tag must match the corresponding VERSION file. Startup stops with a clear error when the values do not match.
Optional ports
These settings have working defaults and only need changing when a port is already in use:
| Variable | Default |
|---|---|
INGEST_PORT | 8001 |
COMMUNICATIONS_PORT | 8002 |
UI_PORT | 3000 |
LITELLM_PORT | 7070 |
You do not need to set API_K8S_KUBECONFIG_PATH. ./run.sh writes the expected in-container kubeconfig path automatically.
Start Agent Barn
Start the complete environment:
./run.shTo start without following logs:
./run.sh --detachThe launcher:
- Confirms Docker exists and the daemon is running.
- Loads
.envand validates required values. - Starts the local k3d cluster and LiteLLM.
- Loads the Hermes and OpenClaw base images into k3d, skipping images already present.
- Configures the API container to use the generated internal kubeconfig.
- Starts PostgreSQL and Redis.
- Builds the API image.
- Runs Alembic migrations.
- Builds and starts the API, worker, Communications, and UI services.
- Follows Compose logs unless detached mode was selected.
Verify the environment
Check the containers
docker compose -f compose.yml psExpected long-running application services:
dbredisapiworkercommunicationsui- LiteLLM services started through the k3d profile
Check the Product API
curl --fail http://localhost:8000/api/v1/healthThis confirms that the Product API can reach PostgreSQL. It does not prove that Ingest, Communications, LiteLLM, or any Agent Runtime is healthy.
Check the Ingest API
curl --fail http://localhost:8001/ingest/v1/openapi.jsonCheck the Communications service
curl --fail http://localhost:8002/healthOptional cluster inspection
If you have kubectl installed, you can inspect the local cluster with the generated host kubeconfig:
kubectl \
--kubeconfig .k3d/kubeconfig-host.yaml \
get namespace agent-farmSign in
Open:
http://localhost:3000 Sign in with the email and password from PLATFORM_ADMIN_CREDENTIALS in .env. From there:
- Create or select an Organization.
- Create a headless Agent.
- Start the Agent.
- Add a Communication Connection separately, when provider messaging is required.
Continue with Create your first Organization, then Hire your first Agent.
Your first local Agent
Hiring creates a headless Agent. Communication is a separate, later step.
- Create an Organization.
- Review the Organization model allowlist and default.
- Hire a headless Agent.
- Choose Hermes or OpenClaw.
- Select a Template.
- Configure required Skills and tool Integration credentials.
- Start the Agent.
- Add one or more Communication Connections.
- Complete provider-side setup.
- Verify Runtime health and Connection health separately.
- Send one allowed provider message.
- Inspect the Connection-scoped Conversation.
- Use a controlled tool request before expecting a Tool Call.
Hiring does not create Slack, Microsoft Teams, Telegram, or Discord configuration. See Connect a Platform for the Connection workflow and Verify your Agent for the layered checks.
What is running
| Service | Local address | Responsibility |
|---|---|---|
| Web app | http://localhost:3000 | User interface |
| Product API | http://localhost:8000/api/v1 | Product and administration operations |
| Ingest API | http://localhost:8001/ingest/v1 | Runtime Tool Call telemetry |
| Communications | http://localhost:8002/communications/v1 | Runtime communication protocol and provider delivery |
| LiteLLM | http://localhost:7070 | Model routing and Agent key usage |
| PostgreSQL | localhost:5432 by default | Agent Barn application database |
A few details matter when you are reading logs or debugging a port:
- Product and Ingest run as separate processes in the
apicontainer. - Communications runs in its own
communicationscontainer. - Containers reach Redis through the Compose network. Redis is also published on host port
6379by default for local development. - LiteLLM uses its own PostgreSQL database, separate from the application database.
- Host ports can differ when the corresponding
.envvalues are changed.
The Communications service
Communications runs from the API image as a separate FastAPI application. It listens on port 8002 by default, and its API is mounted beneath /communications/v1. It:
- Supervises supported provider sessions
- Processes durable inbound and outbound Communication Deliveries
- Exposes the Runtime-neutral Communications protocol
- Persists canonical Conversation Messages
- Maintains Connection health and operational metrics
Agent Runtime pods inside k3d reach it through host.docker.internal. The default local base URL is equivalent to:
http://host.docker.internal:8002/communications/v1COMMUNICATIONS_BASE_URL is configured for Agent Runtime resources and is distinct from the Product API URL.
Who writes Conversations and Tool Calls
The two Activity paths have different writers:
- Ingest stores authenticated Runtime Tool Call telemetry.
- The Communications Gateway writes canonical inbound and outbound Conversation Messages.
- Product API routes provide authorized reads for both.
- Conversation identity includes a Communication Connection.
That separation shapes how local failures look:
- A broken Ingest path can leave Tool Calls empty while provider Conversations continue to work.
- A broken Communications path can prevent provider messaging while Tool Call ingest and the Product API remain healthy.
See Activity, conversations, and runtime telemetry.
How local services reach each other
Application services run in Docker Compose while Agent workloads run in k3d, so Agent Runtime resources reach the application through host-published ports.
Browser to Product API
The browser reaches the web app, and the web app reaches the Product API through the existing local application configuration.
Agent Runtime to Ingest
http://host.docker.internal:8001/ingest/v1Agent Runtime to Communications
http://host.docker.internal:8002/communications/v1Agent Runtime to LiteLLM
http://host.docker.internal:7070These host hops are required because of the split between Compose and k3d. Do not substitute Compose-only DNS names in Agent Runtime configuration; a Compose service name is not resolvable from inside a k3d pod.
For how these resources are assembled, see Runtime assembly and deployment.
Stop or resume the environment
Stop the environment:
./stop.shor:
make stopThe normal stop:
- Stops application containers
- Stops the local k3d cluster
- Stops LiteLLM services
- Preserves PostgreSQL and Redis volumes
- Preserves the k3d cluster for a faster restart
Resume with:
./run.sh --detachTo stop and reset the local cluster:
./stop.sh --cleanor:
make stop-cleanThe clean option:
- Removes the application containers
- Deletes the k3d cluster
- Removes generated
.k3dfiles - Preserves named database and Redis volumes
- Requires Agent Runtime images to be loaded again at the next startup
Success checklist
The local environment is ready when you can confirm:
- Docker is running
.envcontains all required values- PostgreSQL and Redis are healthy
- Migrations completed
- The Product API is reachable on
8000 - Ingest is reachable on
8001 - Communications is reachable on
8002 - The UI is reachable on
3000 - LiteLLM is reachable through the configured local port
- The k3d cluster is running
- The Hermes and OpenClaw images were loaded
- Platform Administrator sign-in works
- An Organization can be created or selected
- A headless Agent can be hired and started
- A Communication Connection can be added separately
- Conversation and Tool Call paths are understood as separate
Troubleshooting
./run.sh reports missing values
Open .env and replace the empty or placeholder values named by the script. Use .env.spec for descriptions, then run ./run.sh again.
Do not remove a variable from the validation list to bypass the error.
Docker is unavailable
Start Docker and confirm Linux containers are enabled. Retry after this succeeds:
docker infoMigration fails
Application services do not complete startup when migrations fail. Review the retained migration output and correct the database or migration issue before rerunning.
Do not repeatedly restart the stack without understanding the schema state.
Startup fails while creating the Platform Administrator
Check PLATFORM_ADMIN_CREDENTIALS. Its password must contain at least eight characters, an uppercase letter, a lowercase letter, and a digit.
The high-level error can appear as:
500: Error while initializing startup dataThe API log immediately before that message contains the underlying validation error.
The Product API works but Tool Calls remain empty
Check Ingest on port 8001, then confirm that Agent Runtime resources use the correct Ingest base URL:
http://host.docker.internal:8001/ingest/v1Also confirm the request actually invoked a tool. A request that uses no tool is not expected to produce a Tool Call.
Do not diagnose this as a Conversation persistence failure.
The Product API works but provider Conversations remain empty
Check Communications on port 8002, then confirm:
- The Agent has an enabled Communication Connection
- Provider credentials and provider-side setup are complete
- The Connection's access and mention policy allow the message
- The Agent Runtime is running
Do not check Ingest as the canonical Conversation writer. See Communication Connections.
A Conversation appears but no reply arrives
Review Agent Runtime health, then review Connection health and delivery diagnostics separately. Confirm outbound provider permissions and that the source Connection remains enabled.
An Agent cannot start in k3d
Confirm that:
- The Runtime images were loaded
- The API container can reach the generated kubeconfig
- k3d is running
Then review model, Template, Skill, and Integration requirements, along with the Agent lifecycle logs.
Communications cannot be reached from an Agent pod
Confirm that:
- The
communicationscontainer is running - Host port
8002is published COMMUNICATIONS_BASE_URLuseshost.docker.internal- A local firewall or Docker networking rule is not blocking the host-published port
On native Linux, also verify that the host firewall permits traffic from the k3d bridge network.
Runtime image tags do not match
OPENCLAW_IMAGE and HERMES_IMAGE must end with the versions stored in:
openclaw-base/VERSION
hermes-base/VERSIONUpdate .env so each tag matches its file exactly.
The Runtime-image build cannot read aai-cli
The runtime Dockerfiles clone the public repository directly. Check network access to GitHub and the public source URL; a GitHub token is not a local runtime-build prerequisite.
An Agent is stuck in ImagePullBackOff
Confirm that the image reference in .env matches the imported image and the repository VERSION file.
Reload missing images with:
bash docker/k3d/k3d-load-images.shAgent pods are killed with exit code 137
Exit code 137 or OOMKilled normally means the Docker environment ran out of memory.
Inspect current usage:
docker stats --no-streamIncrease the Docker memory allocation or stop unrelated workloads before retrying.
Next steps
Microsoft Teams requires a webhook that Microsoft can reach over public HTTPS. A localhost-only installation does not meet that prerequisite. Before following the Microsoft Teams guide, arrange a public endpoint with your installation administrator or prepare a self-hosted deployment.
Cost freshness and synchronization
See Cost synchronization for the entrypoint, credentials, schedule, backfill, and healing behavior.
Calls can occur while Costs remains empty or stale. Check cost-sync scheduling and logs, database access, LiteLLM master-key lookup, and upstream availability. An absent record or unresolved OpenRouter lookup does not prove a call was free. Review attribution and recovery backlog separately from Runtime health.
Docker Compose and run.sh do not schedule cost synchronization automatically. Local reporting needs an explicit invocation in a configured application environment; refreshing Costs does not perform synchronization.