How-to guides / Running Agen: local and distributed
Start here
How-to guides
How-to guides
Running Agen: local and distributed
Local and distributed fleets use the same two binaries (agen,
agen-host) and the same commands; only the configuration differs
(where the Store is, whether the Hub uses TLS, how many Nests join).
Install Agen on every machine that runs a Hub or a Nest, and on clients:
see Install. Agen registers no OS services; run Hubs and Nests
under your service manager (systemd, or the StatefulSets in deploy/kube).
Local: one process
agen up # Hub + scheduler + one Nest, SQLite in ~/.agen; keeps running, so use a second terminal for the rest
agen init # writes the hello example to ./hello
agen deploy hello --replicas 1
agen run hello "hi"
agen ps --all # deployments and instances
agen ui # sign-in link for the web UI
agen down # stops the Hub, the Nest and every agent
Distributed: Hubs, Nests, Postgres
Same commands, different flags:
# Hub (any number of replicas; one is leader, the rest stand by)
# AGEN_HUB_KEK (same on every Hub, never given to Nests) seals the Hub's
# CA and call-token keys in the Store.
AGEN_ADMIN_TOKEN=... AGEN_HUB_KEK=... agen hub serve --tls --listen 0.0.0.0:7070 \
--tls-host hub.example --store postgres://agen@db/agen
# prints: CA sha256:<hash>
# A Store role for Nests and their hosts: run-data tables only (no access to
# the Hub's keys or tokens). Give Nests this role's URL, not the admin one.
AGEN_HOST_DB_PASSWORD=... agen store host-role --store postgres://admin@db/agen --role agen_host
# A join token per Nest (single use), with the CA hash to pin
agen join-token --hub https://hub.example:7070 --token $AGEN_ADMIN_TOKEN --ca-hash sha256:<hash>
# Each Nest (Manager + Gateway + agent processes)
agen nest run --hub https://hub.example:7070 --join-token <token> --ca-hash sha256:<hash> \
--name nest1 --store postgres://agen_host@db/agen \
--gateway-listen 0.0.0.0:7071 --gateway-url http://nest1:7071
# Clients: the same fleet commands, pointed at the Hub
export AGEN_HUB=https://hub.example:7070 AGEN_TOKEN=... AGEN_CA_HASH=sha256:<hash>
agen deploy hello --replicas 3 # a bundle directory, e.g. from agen init
agen ps --all
The leader Hub deletes spans, log lines and trigger events older than
--retention (default 30 days; 0 keeps them). See Traces.
Moving a local fleet to Postgres keeps its history:
agen down && agen migrate --to postgres://…, then start the Hub with
--store postgres://….
deploy/compose/cluster.yml runs a complete distributed fleet in Docker
(Postgres, two Hubs behind a TLS-passthrough load balancer, three Nests);
platform/e2e tests it end to end, including a Nest dying mid-run, the
leader Hub failing over and both Hubs being down.
Secrets
Bundles name their secrets in x-agen/secrets.json; values never go in the
bundle. With "source": "platform" the value comes from the fleet:
printf %s "$OPENROUTER_API_KEY" | agen secret set OPENROUTER_API_KEY --for my-agent -n default
agen secret ls -n default # names only; values are never shown
agen secret rm OPENROUTER_API_KEY -n default
A Nest gets a value only for a deployment it runs that declares it and that the secret names (--for). With
AGEN_HUB_KEK set, values are sealed in the Store.
Key rotation
See Rotation: the KEK is rotated without downtime by restarting the Hubs with both keys before re-sealing.
Kubernetes
deploy/kube runs the Hub (two replicas, mTLS) in namespace agen, and a
Nest whose agents run as pods (--backend kubernetes) in namespace
agen-nests. They are separate because a Nest may create pods, and a pod
can mount any secret of its namespace: the Hub’s admin token must not be
one of them. With kind:
docker build -f deploy/docker/Dockerfile -t agen:dev .
docker pull postgres:17-alpine
kind create cluster --config deploy/kube/kind.yaml
# Load the images. On Docker Desktop, `kind load docker-image` can fail for
# multi-platform images; importing into the node directly works:
docker save agen:dev | docker exec -i kind-control-plane ctr -n k8s.io images import --snapshotter=overlayfs -
docker save postgres:17-alpine | docker exec -i kind-control-plane ctr -n k8s.io images import --snapshotter=overlayfs -
kubectl apply -f deploy/kube/hub.yaml -f deploy/kube/postgres-dev.yaml # postgres-dev: throwaway DB
kubectl -n agen create secret generic agen-admin --from-literal=token=<admin token>
kubectl -n agen create secret generic agen-store --from-literal=url=postgres://agen:agen@postgres.agen.svc:5432/agen
kubectl -n agen logs -l app=agen-hub | grep CA # the CA hash
agen join-token --hub https://127.0.0.1:17444 --token <admin token> --ca-hash sha256:<hash>
kubectl create namespace agen-nests
# Nests get a least-privilege Store role (run-data tables only), made from a Hub pod:
kubectl -n agen exec deploy/agen-hub -- env AGEN_HOST_DB_PASSWORD=<pw> agen store host-role --role agen_host
kubectl -n agen-nests create secret generic agen-store --from-literal=url=postgres://agen_host:<pw>@postgres.agen.svc:5432/agen
kubectl -n agen-nests create secret generic agen-join --from-literal=token=<join token> --from-literal=ca-hash=sha256:<hash>
# Optional: variables every agent instance gets, e.g. a provider key.
kubectl -n agen-nests create secret generic agen-host-env --from-literal=OPENROUTER_API_KEY=<key>
kubectl apply -f deploy/kube/nest.yaml
agen deploy hello --hub https://127.0.0.1:17444 --token <admin token> --ca-hash sha256:<hash>
On a host whose Docker uses cgroup v1 (older WSL2 kernels), recent kubelets do not start; add this to the kind config:
kubeadmConfigPatches:
- |
kind: KubeletConfiguration
failCgroupV1: false
agen scale, agen run, agen ps and the rest are unchanged; each
instance shows up as a pod labelled agen.dev/deployment=<name>, limited by
--kube-cpu-limit / --kube-memory-limit (1 CPU, 1Gi by default).
Instances only answer their Manager (a per-instance host token); the
NetworkPolicy in nest.yaml adds network isolation where the CNI enforces
it (kind’s default CNI does not). Bundles must fit in a ConfigMap (700 KiB).
platform/e2e (TestKubernetesBackend) tests this on kind: scaling, a
50-task burst, scale to zero and wake on an A2A call, a deleted instance
pod, a deleted Nest pod, host isolation and provider keys.
Local (agen up) |
Distributed | |
|---|---|---|
| Binaries | agen, agen-host |
same |
| Store | SQLite in ~/.agen |
Postgres |
| Hub ↔ Nest | HTTP on localhost, nest bearer token | HTTPS, Hub-issued Nest certificates (mTLS) |
| Hubs | 1 (in process) | 1+ (leader election in the Store) |
| Nests | 1 (in process) | any number, agen nest run |
| Commands | agen deploy/scale/ps/run/logs/stop/rm/... |
same (+ --hub/--token/--ca-hash) |