nanobot/DOCKER_MULTI_BOT_GUIDE.md
tanyar09 4b808f9a30 Docs: MCP local clones and tool profiles
Document local-cloned MCP server layout, docker mounts, tool-call JSON protocol for local providers, profile routing behavior, and common gotchas. Add a brief pointer from the multi-bot Docker guide.

Made-with: Cursor
2026-03-31 12:53:34 -04:00

16 KiB
Raw Blame History

Docker Multi-Bot Setup Guide

Complete guide for running multiple nanobot instances with Docker, each with its own Telegram bot.

📋 Table of Contents

  1. Quick Start
  2. Architecture Overview
  3. Setup Instructions
  4. Running Bots
  5. Configuration Management
  6. Development Workflow
  7. Troubleshooting
  8. Reference

Quick Start

Start Only User1 Bot

docker compose -f docker-compose.multi.env.yml up -d nanobot-user1

Start All Bots

docker compose -f docker-compose.multi.env.yml up -d

Stop All Bots

docker compose -f docker-compose.multi.env.yml down

Stop Specific Bot

docker compose -f docker-compose.multi.env.yml stop nanobot-user1

View Logs

docker logs -f nanobot-user1

Architecture Overview

How It Works

Each bot runs in its own Docker container with:

  • Separate config directory: ~/.nanobot-user1, ~/.nanobot-user2, etc.
  • Shared env file: .env.shared (common settings)
  • Bot-specific env file: .env.user1, .env.user2, etc. (overrides)
  • Isolated environment: No conflicts between bots

File Structure

nanobot/
├── .env.shared              # Shared settings (API keys, model, etc.)
├── .env.user1              # Bot 1 (@ilia) overrides
├── .env.user2              # Bot 2 (@family) overrides
├── .env.user3              # Bot 3 (@wife) overrides
├── agent_workspaces/       # Templates copied by scripts/init-agent-workspaces.sh
├── scripts/init-agent-workspaces.sh
├── docker-compose.multi.env.yml      # Production compose file
├── docker-compose.multi.dev.yml      # Development compose file
│
├── ~/.nanobot/workspaces/
│   ├── ilia/               # Mounted as /workspace for user1 — AGENTS.md, memory/, …
│   ├── family/             # user2
│   └── wife/               # user3
├── ~/.nanobot-user1/
│   └── config.json         # Bot 1 channel config (Telegram token, allowFrom)
├── ~/.nanobot-user2/
│   └── config.json         # Bot 2 channel config
└── ~/.nanobot-user3/
    └── config.json         # Bot 3 channel config

./workspace in the repo remains for single-bot docker-compose.yml only; multi-bot uses ~/.nanobot/workspaces/* per container.

Configuration Loading

  1. Docker Compose loads environment files:

    • First: .env.shared (shared settings)
    • Second: .env.userX (bot-specific overrides)
    • Later files override earlier ones
  2. Container mounts config directory:

    • Host: ~/.nanobot-user1 → Container: /root/.nanobot
  3. Nanobot loads:

    • Environment variables (from Docker env files)
    • Config file: /root/.nanobot/config.json (mounted from host)

MCP servers + tool profiles (local LLM note)

If youre using a local LLM provider with a low tool limit (often ~20 tools), MCP servers (which can register 30+ tools) can exceed the limit unless you use tool profiles carefully.

See docs/mcp_local_clone_and_tool_profiles.md for:

  • Local-clone MCP layout (./mcp-servers/app/mcp-servers)
  • How MCP env vars like $NANOBOT_GITLE_TOKEN are expanded into server env
  • Tool-call JSON protocol for local providers
  • Profile routing behavior and when MCP servers disconnect

Setup Instructions

Step 0: Per-agent workspaces (personalities + isolated memory)

Multi-bot compose mounts separate workspace directories so each bot has its own AGENTS.md, SOUL.md, USER.md, and memory/ (no shared ./workspace).

On the host, from the repo root:

./scripts/init-agent-workspaces.sh

This creates:

~/.nanobot/workspaces/
  ilia/       # nanobot-user1 — dev / infra persona
  family/     # nanobot-user2 — household persona
  wife/       # nanobot-user3 — personal assistant persona

Templates live in-repo under agent_workspaces/. Re-run the script anytime: it skips files that already exist. Adjust ownership if Docker runs as root:

sudo chown -R "$(whoami):$(whoami)" ~/.nanobot/workspaces

Step 1: Create Environment Files

Run the setup script:

./env-files-setup.sh

This creates:

  • .env.shared - Shared settings
  • .env.user1, .env.user2, .env.user3 - Bot-specific overrides

Step 2: Edit .env.shared

Add your shared settings:

nano .env.shared

Example:

# Provider Settings
NANOBOT_PROVIDERS__CUSTOM__API_KEY=no-key
NANOBOT_PROVIDERS__CUSTOM__API_BASE=http://172.17.0.1:11434/v1

# Agent Settings
NANOBOT_AGENTS__DEFAULTS__MODEL=llama3.1:8b
NANOBOT_AGENTS__DEFAULTS__WORKSPACE=/mnt/data/nanobot
NANOBOT_AGENTS__DEFAULTS__TEMPERATURE=0.7

Important: Use 172.17.0.1 (Docker bridge gateway) instead of localhost so containers can reach Ollama on the host.

Step 3: Edit Bot-Specific Env Files

Edit .env.user1, .env.user2, etc. with bot-specific settings:

nano .env.user1

Example:

# Telegram Bot Token
NANOBOT_CHANNELS__TELEGRAM__ENABLED=true
NANOBOT_CHANNELS__TELEGRAM__TOKEN=your_bot_token_here

# Email Credentials (if different per bot)
NANOBOT_CHANNELS__EMAIL__IMAP_USERNAME=bot1@example.com
NANOBOT_CHANNELS__EMAIL__IMAP_PASSWORD=password

Step 4: Create Config Files

Run the config creation script:

./create-bot-configs.sh

Or manually create ~/.nanobot-user1/config.json:

{
  "channels": {
    "telegram": {
      "enabled": true,
      "allowFrom": ["YOUR_TELEGRAM_USERNAME"]
    },
    "email": {
      "enabled": true,
      "allowFrom": ["email@example.com"]
    }
  }
}

Note: allowFrom arrays must be in config.json, NOT in env files.

Step 5: Build Docker Image

docker compose -f docker-compose.multi.env.yml build

Running Bots

Start Commands

# Start only user1
docker compose -f docker-compose.multi.env.yml up -d nanobot-user1

# Start only user2
docker compose -f docker-compose.multi.env.yml up -d nanobot-user2

# Start only user3
docker compose -f docker-compose.multi.env.yml up -d nanobot-user3

# Start all bots
docker compose -f docker-compose.multi.env.yml up -d

Stop Commands

# Stop only user1
docker compose -f docker-compose.multi.env.yml stop nanobot-user1

# Stop only user2
docker compose -f docker-compose.multi.env.yml stop nanobot-user2

# Stop only user3
docker compose -f docker-compose.multi.env.yml stop nanobot-user3

# Stop all bots
docker compose -f docker-compose.multi.env.yml down

Restart Commands

# Restart only user1
docker compose -f docker-compose.multi.env.yml restart nanobot-user1

# Restart all bots
docker compose -f docker-compose.multi.env.yml restart

Status Commands

# Check what's running
docker compose -f docker-compose.multi.env.yml ps

# View logs for user1
docker logs -f nanobot-user1

# View logs for all bots
docker compose -f docker-compose.multi.env.yml logs -f

# View logs for specific bot
docker compose -f docker-compose.multi.env.yml logs -f nanobot-user1

Remove Commands

# Stop and remove user1 container
docker compose -f docker-compose.multi.env.yml stop nanobot-user1
docker rm nanobot-user1

# Stop and remove all containers
docker compose -f docker-compose.multi.env.yml down

# Remove containers and volumes (keeps config files)
docker compose -f docker-compose.multi.env.yml down -v

Configuration Management

Updating Shared Settings

Edit .env.shared:

nano .env.shared

Restart affected containers:

docker compose -f docker-compose.multi.env.yml restart

Updating Bot-Specific Settings

Edit .env.userX:

nano .env.user1

Restart that specific bot:

docker compose -f docker-compose.multi.env.yml restart nanobot-user1

Updating Config Files

Edit config file:

nano ~/.nanobot-user1/config.json

Restart container:

docker restart nanobot-user1

Adding Email to allowFrom

# Add email to user1's allowFrom
jq '.channels.email.allowFrom += ["newemail@example.com"]' \
  ~/.nanobot-user1/config.json > ~/.nanobot-user1/config.json.tmp && \
  mv ~/.nanobot-user1/config.json.tmp ~/.nanobot-user1/config.json

# Restart container
docker restart nanobot-user1

Environment Variable Format

Nanobot uses Pydantic's BaseSettings with:

  • Prefix: NANOBOT_
  • Nested delimiter: __ (double underscore)

Examples:

  • NANOBOT_PROVIDERS__CUSTOM__API_KEYproviders.custom.apiKey
  • NANOBOT_CHANNELS__TELEGRAM__TOKENchannels.telegram.token
  • NANOBOT_AGENTS__DEFAULTS__MODELagents.defaults.model

Development Workflow

Use docker-compose.multi.dev.yml which mounts source code:

# Start in development mode
docker compose -f docker-compose.multi.dev.yml up -d nanobot-user1

# Make changes in venv
source venv/bin/activate
nano nanobot/channels/telegram.py

# Restart container (picks up changes)
docker compose -f docker-compose.multi.dev.yml restart nanobot-user1

# View logs
docker logs -f nanobot-user1-dev

Option 2: Rebuild After Changes

# Make changes
source venv/bin/activate
nano nanobot/channels/telegram.py

# Rebuild image
docker compose -f docker-compose.multi.env.yml build nanobot-user1

# Recreate container
docker compose -f docker-compose.multi.env.yml up -d --force-recreate nanobot-user1

Option 3: Run on Host (Not Docker)

# Activate venv
source venv/bin/activate

# Run directly
nanobot gateway

Note: Host command uses:

  • Config: ~/.nanobot/config.json (original)
  • .env: .env in current directory (NOT .env.shared)

Troubleshooting

Bot Not Responding

  1. Check if container is running:

    docker ps | grep nanobot-user1
    
  2. Check logs:

    docker logs nanobot-user1 --tail 50
    
  3. Verify Telegram token:

    grep TELEGRAM__TOKEN .env.user1
    
  4. Check allowFrom:

    cat ~/.nanobot-user1/config.json | jq '.channels.telegram.allowFrom'
    

Connection Error (Ollama)

Problem: "Connection error" when bot tries to use LLM

Solution:

  1. Check Ollama is running:

    curl http://localhost:11434/api/tags
    
  2. Verify API_BASE in .env.shared:

    grep API_BASE .env.shared
    

    Should be: http://172.17.0.1:11434/v1 (NOT localhost)

  3. Restart container:

    docker restart nanobot-user1
    

Config Not Loading

  1. Check volume mount:

    docker inspect nanobot-user1 | grep -A 5 Mounts
    
  2. Verify config exists:

    ls -lh ~/.nanobot-user1/config.json
    
  3. Check inside container:

    docker exec nanobot-user1 cat /root/.nanobot/config.json
    

Environment Variables Not Applied

  1. Check env files are loaded:

    docker exec nanobot-user1 env | grep NANOBOT
    
  2. Recreate container (env files loaded at creation):

    docker compose -f docker-compose.multi.env.yml up -d --force-recreate nanobot-user1
    

Port Already in Use

If port conflicts:

# Check what's using the port
sudo lsof -i :18790

# Stop conflicting container
docker stop <container_name>

# Or change port in docker-compose.multi.env.yml

Reference

Docker Compose Files

File Purpose Use Case Env Loading
docker-compose.yml Single-bot baseline One gateway + optional CLI, simplest setup No env_file; uses mounted ~/.nanobot/config.json and container environment
docker-compose.multi.yml Multi-bot baseline Multiple bots with separate config dirs, minimal env indirection No env_file; each bot uses mounted ~/.nanobot-userX/config.json and container environment
docker-compose.multi.env.yml Multi-bot production Stable multi-bot deployments with shared + per-bot overrides Loads .env.shared first, then .env.userX (later file overrides earlier)
docker-compose.multi.dev.yml Multi-bot development Active code development with source mounted into containers Same env behavior as multi.env (.env.shared + .env.userX)

Container Names

  • nanobot-user1 - Bot 1 container (production)
  • nanobot-user1-dev - Bot 1 container (development)
  • nanobot-user2 - Bot 2 container
  • nanobot-user3 - Bot 3 container

Ports

  • User 1: 18790 (host) → 18790 (container)
  • User 2: 18791 (host) → 18790 (container)
  • User 3: 18792 (host) → 18790 (container)

Config Locations

Location Used By Purpose
~/.nanobot/config.json Host nanobot gateway Original config (not used by Docker)
~/.nanobot-user1/config.json Docker user1 Bot 1 config (mounted into container)
~/.nanobot-user2/config.json Docker user2 Bot 2 config
~/.nanobot-user3/config.json Docker user3 Bot 3 config

Environment Files

File Loaded By Purpose
.env.shared All Docker containers Shared settings (API keys, model, etc.)
.env.user1 Docker user1 only Bot 1 specific overrides
.env.user2 Docker user2 only Bot 2 specific overrides
.env.user3 Docker user3 only Bot 3 specific overrides
.env Host nanobot gateway Host environment (not used by Docker)

Quick Command Reference

# === START ===
docker compose -f docker-compose.multi.env.yml up -d nanobot-user1  # Start user1
docker compose -f docker-compose.multi.env.yml up -d                # Start all

# === STOP ===
docker compose -f docker-compose.multi.env.yml stop nanobot-user1   # Stop user1
docker compose -f docker-compose.multi.env.yml down                 # Stop all

# === RESTART ===
docker compose -f docker-compose.multi.env.yml restart nanobot-user1  # Restart user1
docker compose -f docker-compose.multi.env.yml restart                 # Restart all

# === LOGS ===
docker logs -f nanobot-user1                                        # View logs
docker compose -f docker-compose.multi.env.yml logs -f nanobot-user1  # View logs

# === STATUS ===
docker compose -f docker-compose.multi.env.yml ps                    # List containers
docker ps | grep nanobot                                            # List containers

# === REBUILD ===
docker compose -f docker-compose.multi.env.yml build nanobot-user1  # Rebuild user1
docker compose -f docker-compose.multi.env.yml build                 # Rebuild all

# === RECREATE (after config changes) ===
docker compose -f docker-compose.multi.env.yml up -d --force-recreate nanobot-user1

Common Tasks

Add a New Bot (User4)

  1. Create config directory:

    mkdir -p ~/.nanobot-user4
    
  2. Create config file:

    cat > ~/.nanobot-user4/config.json << 'EOF'
    {
      "channels": {
        "telegram": {
          "enabled": true,
          "allowFrom": ["USERNAME"]
        }
      }
    }
    EOF
    
  3. Create env file:

    cp .env.user1 .env.user4
    nano .env.user4  # Edit with bot-specific settings
    
  4. Add to docker-compose.multi.env.yml:

    nanobot-user4:
      # ... (copy from nanobot-user1, change port to 18793)
    
  5. Start:

    docker compose -f docker-compose.multi.env.yml up -d nanobot-user4
    

Update Ollama API Base

If Ollama IP changes:

# Edit .env.shared
sed -i 's|http://172.17.0.1:11434|http://NEW_IP:11434|g' .env.shared

# Restart all containers
docker compose -f docker-compose.multi.env.yml restart

Backup Configurations

# Backup all configs
tar -czf nanobot-configs-backup-$(date +%Y%m%d).tar.gz \
  ~/.nanobot-user* \
  .env.shared .env.user*

# Restore
tar -xzf nanobot-configs-backup-YYYYMMDD.tar.gz

Notes

  • Original config (~/.nanobot/config.json) is NOT used by Docker containers
  • Host venv is NOT needed to run Docker commands (Docker builds its own environment)
  • Environment variables override config file settings
  • Arrays (like allowFrom) must be in config.json, NOT in env files
  • Ollama API_BASE must use 172.17.0.1 (Docker bridge) not localhost

See Also

  • ENV_FILES_GUIDE.md - Detailed env file management
  • DEVELOPMENT_WITH_DOCKER.md - Development workflow details
  • SETUP_SUMMARY.md - Initial setup summary
  • VERIFY_DOCKER_SETUP.md - Verification checklist