Docker Compose is the recommended way to deploy Nanobot for production use. This guide covers Docker deployment with persistent configuration.
Nanobot does not have an official Docker Hub image. You build the image locally from the repository.
| Build Method | Image Name | Size |
|---|---|---|
| Local build | nanobot (from Dockerfile) |
~500 MB |
| Base image | ghcr.io/astral-sh/uv:python3.12-bookworm-slim |
- |
git clone https://github.com/HKUDS/nanobot
cd nanobot
# Run onboarding to create configuration
docker compose run --rm nanobot-cli onboard
# This creates ~/.nanobot/config.json
Edit ~/.nanobot/config.json:
{
"providers": {
"openrouter": {
"apiKey": "sk-or-v1-your-openrouter-key"
}
},
"agents": {
"defaults": {
"model": "anthropic/claude-opus-4-5",
"provider": "openrouter"
}
},
"channels": {
"telegram": {
"enabled": true,
"token": "YOUR_TELEGRAM_BOT_TOKEN",
"allowFrom": ["YOUR_USER_ID"]
}
}
}
# Start all services (gateway + API server)
docker compose up -d
# Or start gateway only
docker compose up -d nanobot-gateway
# Check status
docker compose ps
# View logs
docker compose logs -f nanobot-gateway
# Test CLI command
docker compose run --rm --profile cli nanobot-cli agent -m "Hello!"
# Test API server (new in v0.1.5)
curl http://127.0.0.1:8900/health
Access the gateway at http://localhost:18790 and the OpenAI-compatible API server at http://127.0.0.1:8900.
The official docker-compose.yml:
x-common-config: &common-config
build:
context: .
dockerfile: Dockerfile
volumes:
- ~/.nanobot:/home/nanobot/.nanobot
cap_drop:
- ALL
cap_add:
- SYS_ADMIN
security_opt:
- apparmor=unconfined
- seccomp=unconfined
services:
nanobot-gateway:
container_name: nanobot-gateway
<<: *common-config
command: ["gateway"]
restart: unless-stopped
ports:
- 18790:18790
deploy:
resources:
limits:
cpus: "1"
memory: 1G
reservations:
cpus: "0.25"
memory: 256M
nanobot-api:
container_name: nanobot-api
<<: *common-config
command: ["serve", "--host", "0.0.0.0", "-w", "/home/nanobot/.nanobot/api-workspace"]
restart: unless-stopped
ports:
- 127.0.0.1:8900:8900
deploy:
resources:
limits:
cpus: "1"
memory: 1G
reservations:
cpus: "0.25"
memory: 256M
nanobot-cli:
<<: *common-config
profiles:
- cli
command: ["status"]
stdin_open: true
tty: true
Services:
The official Dockerfile:
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
# Install Node.js 20 for the WhatsApp bridge
RUN apt-get update && \
apt-get install -y --no-install-recommends curl ca-certificates gnupg git bubblewrap openssh-client && \
mkdir -p /etc/apt/keyrings && \
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" > /etc/apt/sources.list.d/nodesource.list && \
apt-get update && \
apt-get install -y --no-install-recommends nodejs && \
apt-get purge -y gnupg && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Install Python dependencies first (cached layer)
COPY pyproject.toml README.md LICENSE ./
RUN mkdir -p nanobot bridge && touch nanobot/__init__.py && \
uv pip install --system --no-cache . && \
rm -rf nanobot bridge
# Copy the full source and install
COPY nanobot/ nanobot/
COPY bridge/ bridge/
RUN uv pip install --system --no-cache .
# Build the WhatsApp bridge
WORKDIR /app/bridge
RUN git config --global --add url."https://github.com/".insteadOf ssh://git@github.com/ && \
git config --global --add url."https://github.com/".insteadOf git@github.com: && \
npm install && npm run build
WORKDIR /app
# Create non-root user and config directory
RUN useradd -m -u 1000 -s /bin/bash nanobot && \
mkdir -p /home/nanobot/.nanobot && \
chown -R nanobot:nanobot /home/nanobot /app
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN sed -i 's/\r$//' /usr/local/bin/entrypoint.sh && chmod +x /usr/local/bin/entrypoint.sh
USER nanobot
ENV HOME=/home/nanobot
# Gateway default port
EXPOSE 18790
ENTRYPOINT ["entrypoint.sh"]
CMD ["status"]
Key Dockerfile features:
nanobot, UID 1000) — production hardening (v0.1.5)entrypoint.sh for initialization — new in v0.1.5docker build -t nanobot .
docker run -d \
--name nanobot-gateway \
-p 18790:18790 \
-v ~/.nanobot:/home/nanobot/.nanobot \
nanobot gateway
# Check status
docker run --rm -v ~/.nanobot:/home/nanobot/.nanobot nanobot status
# Run agent command
docker run --rm -v ~/.nanobot:/home/nanobot/.nanobot nanobot agent -m "Hello!"
# Onboarding (first time)
docker run --rm -v ~/.nanobot:/home/nanobot/.nanobot nanobot onboard
Configuration is persisted in:
~/.nanobot/ - Configuration directory
config.json - Main configuration fileworkspace/ - Agent workspace (scheduled tasks, etc.)# Pull latest changes
cd nanobot
git pull
# Rebuild image
docker compose build
# Restart services
docker compose down
docker compose up -d
docker compose ps
# All services
docker compose logs -f
# Gateway only
docker compose logs -f nanobot-gateway
# CLI output
docker compose run --rm nanobot-cli status
Gateway won’t start:
# Check configuration
cat ~/.nanobot/config.json
# Verify API keys are set
docker compose run --rm nanobot-cli status
WhatsApp channel issues:
# Verify Node.js is installed in container
docker compose exec nanobot-gateway node --version
# Should output: v20.x.x
Permission errors:
# Fix directory permissions
chmod -R 755 ~/.nanobot
For production environments:
~/.nanobot~/.nanobotserver {
listen 443 ssl http2;
server_name nanobot.example.com;
ssl_certificate /etc/ssl/certs/nanobot.example.com.crt;
ssl_certificate_key /etc/ssl/private/nanobot.example.com.key;
location / {
proxy_pass http://localhost:18790;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
💼 Professional Services: Need expert help with your Nanobot Docker deployment? We offer consulting, training, and support. Contact our team →