Skip to content

Running multiple agents-fleet instances on one machine

Operators frequently want to drive several repositories from a single host — one fleet per project — each with its own Telegram bot, its own inbox queue, its own intel database, and zero collisions between them.

As of Phase 1 of T-169 (feat/multi-instance-phase-1-fleet-home) this works via the existing FLEET_HOME environment variable. The Telegram and inbox runtime wiring now route through getHomeForLegacySubsystems() instead of bypassing FLEET_HOME and hardcoding os.homedir().

The contract

Set FLEET_HOME to an absolute path whose basename is exactly .fleet. This single constraint keeps the per-process state fully isolated.

bash
# Project A
export FLEET_HOME="$HOME/project-a/.fleet"
export AGENTS_FLEET_TELEGRAM_TOKEN="<bot-a-token-from-BotFather>"
agents-fleet --telegram --inbox

# Project B (separate terminal, separate repo)
export FLEET_HOME="$HOME/project-b/.fleet"
export AGENTS_FLEET_TELEGRAM_TOKEN="<bot-b-token-from-BotFather>"
agents-fleet --telegram --inbox

What this gives you, per instance:

StatePath
Telegram pairings$FLEET_HOME/telegram.json
Inbox file queue$FLEET_HOME/inbox/
Inbound attachments$FLEET_HOME/attachments/<sessionId>/
Intel SQLite DB$FLEET_HOME/intel/fleet-intel.db
Sessions$FLEET_HOME/sessions/
Logs$FLEET_HOME/logs/
Plans / shadows$FLEET_HOME/plans/, $FLEET_HOME/shadows/

Each Telegram bot must use its own token — talk to @BotFather and create a second bot for the second instance. The token is the trust boundary; reusing one token across two agents-fleet --telegram processes would cause both to compete for the same Telegram update stream.

Why the basename must be .fleet

The bot module (src/bot/) and inbox setup (src/cli/inboxSetup.ts) both compute their on-disk root as path.join(home, '.fleet', ...). The Phase 1 fix introduces a thin helper that returns path.dirname(FLEET_HOME) so that this join round-trips back to your configured FLEET_HOME — but only when the configured path ends in /.fleet.

If you set FLEET_HOME=$HOME/project-a/myfleet, the telegram + inbox subsystems will still write to $HOME/project-a/.fleet/..., which is probably not what you wanted. The other subsystems (intel DB, sessions, plans, logs) honor FLEET_HOME verbatim and would write to $HOME/project-a/myfleet/... — a split-brain layout that produces hard- to-debug inconsistencies.

A deeper refactor that renames the pervasive home: string convention inside src/bot/ to fleetHome: string (so any FLEET_HOME basename works) is tracked as Phase 1b of T-169.

Defense in depth: SQLite

Phase 1 also tightens the intel database for the multi-process case (src/intel/SchemaManager.ts):

  • journal_mode = WAL (already present) — concurrent readers + 1 writer
  • busy_timeout = 5000 — wait 5 s for the lock instead of returning SQLITE_BUSY immediately
  • synchronous = NORMAL — recommended durability tier for WAL

Two agents-fleet instances pointed at the same intel DB will no longer error out; they will queue politely. (Pointing two instances at the same DB is not the intended use case — each instance should have its own FLEET_HOME — but the pragmas harden the path so an accidental collision degrades gracefully.)

Out of scope (Phase 2 / Phase 3)

  • Surfacing the instance name in the Telegram message footer so a user who paired multiple bots can tell them apart from a single chat.
  • An explicit --instance <name> flag that infers FLEET_HOME from a per-user catalog (~/.fleet/instances/<name>/...).
  • Cross-machine N-to-1 operator UX (multiple operators driving one fleet, multiple fleets driven by one operator).

These are tracked in follow-up tickets under T-169.