1. Зачем каждому юзеру свой AI-агент
Представьте SaaS, где каждый клиент получает персонального AI-ассистента в Telegram. Не чат-бота с кнопками, а полноценного агента: он помнит контекст, работает с файлами, ищет в интернете, выполняет код и адаптируется под конкретного человека.
В 2026 году это реальный продукт. OpenClaw — open-source AI Gateway — позволяет запустить такого агента за минуты. Один процесс, один конфиг, один Telegram-бот — и у вас личный Джарвис. Вопрос в другом: как масштабировать это с одного юзера до пятисот?
Почему multi-tenant на одном процессе не работает
OpenClaw поддерживает agents system — несколько агентов на одном gateway. Казалось бы, идеально. Но для SaaS этот подход провалится:
| Аспект | Один gateway, много агентов | Отдельные процессы |
|---|---|---|
| Изоляция данных | ❌ Общий workspace | ✅ Полная |
| Concurrent limit | 4 сессии (shared) | Без ограничений |
| Telegram-боты | ❌ Один бот на всех | ✅ У каждого свой |
| Безопасность | ❌ Tool access shared | ✅ Sandboxed |
| Отказоустойчивость | ❌ Один падает — ломает всех | ✅ Изолированные сбои |
| Кастомизация | Через agents config | Полная свобода |
Ограничение в 4 concurrent сессии, общий workspace и один Telegram-бот на всех делают multi-agent подход непригодным для коммерческого SaaS. Правильная модель: один OpenClaw процесс = один пользователь.
Что получает каждый юзер
Типичный профиль: юзер активен 10–20% времени (2–5 часов/день), отправляет 20–50 сообщений. В остальное время процесс спит, потребляя ~60 MB RAM. Пиковая нагрузка — утро 9–11 и вечер 19–22 МСК.
2. Архитектура «один процесс = один юзер»
Общая схема
Как это работает
1. Telegram отправляет webhook на единый домен: https://api.your-saas.com/webhook/{bot_token}. Каждый юзер создал своего бота через @BotFather — у каждого уникальный токен.
2. Webhook Router — легковесный reverse proxy (Nginx с lua или Node.js на ~30 строк) — смотрит на bot_token в URL, находит порт нужного OpenClaw-инстанса и проксирует запрос.
3. OpenClaw-процесс обрабатывает сообщение: загружает контекст сессии, отправляет запрос к LLM API (DeepSeek, Gemini и т.д.), выполняет tool calls, возвращает ответ юзеру.
4. LLM API — все инстансы используют один набор API-ключей. Стоимость отслеживается через логи: сколько токенов потратил каждый юзер. Это позволяет аллоцировать расходы и выставлять счета.
Файловая структура
/data/users/
user_001/
openclaw.json # конфиг с уникальным bot token
AGENTS.md # персональный system prompt
workspace/ # файлы юзера
MEMORY.md
memory/
2026-02-16.md
user_002/
openclaw.json
AGENTS.md
workspace/
...
user_500/
openclaw.json
AGENTS.md
workspace/
Каждый юзер — полностью изолированная директория. Шаблон openclaw.json копируется при регистрации, с подстановкой уникального botToken и порта.
3. Mac Mini vs VPS vs Cloud
Mac Mini M4 Pro — то, что уже есть
12 ядер (10P + 2E), 24 GB unified memory, 512 GB SSD. Ключевые расчёты:
| Параметр | Значение |
|---|---|
| Доступная RAM (за вычетом системы) | ~20 GB |
| RAM на процесс (idle) | ~60 MB |
| RAM на процесс (активный) | ~180 MB |
| Макс. процессов (все idle) | ~300 |
| Макс. процессов (20% активных) | ~130 |
| Реалистичный лимит | 80–100 инстансов |
Формула: 20 GB / (0.8 × 60 + 0.2 × 180) = 20000 / 84 ≈ 238. С учётом overhead (Node.js GC, tmpfs, логи) — реалистично 80–100 юзеров. CPU не bottleneck: LLM-вызовы — это network I/O, а не вычисления.
VPS — горизонтальное масштабирование
| Провайдер | Тариф | vCPU | RAM | Цена/мес | OC-инстансов |
|---|---|---|---|---|---|
| Hetzner (EU) | CX22 | 2 | 4 GB | €3.79 (~₽400) | 10–15 |
| Hetzner (EU) | CX32 | 4 | 8 GB | €6.80 (~₽720) | 25–35 |
| Hetzner (EU) | CX42 | 8 | 16 GB | €13.60 (~₽1440) | 50–70 |
| Hetzner ARM | CAX21 | 4 | 8 GB | €6.40 (~₽680) | 25–35 |
| Timeweb (РФ) | MSK 50 | 2 | 4 GB | ₽720 | 10–15 |
| Timeweb (РФ) | MSK 100 | 8 | 12 GB | ₽2745 | 40–55 |
| DigitalOcean | Basic | 4 | 8 GB | $48 (~₽4800) | 25–35 |
Сравнительная таблица
| Критерий | Mac Mini (дома) | VPS (Hetzner) | Гибрид |
|---|---|---|---|
| Стоимость запуска | ₽0 (уже есть) | €4–7/мес | ₽0 + €4/мес |
| Макс. юзеров | 80–100 | ∞ (горизонтально) | ∞ |
| Стоимость/юзер | ₽70–112 | ₽49–60 | ₽50–80 |
| Uptime | 95–98% | 99.9% | 99.5%+ |
| Отказоустойчивость | ❌ Single point | ⭐⭐ | ⭐⭐⭐ |
| Сложность | ⭐ Низкая | ⭐⭐ Средняя | ⭐⭐⭐ |
| Docker native | ❌ Через VM | ✅ Linux | ✅ |
| Time to market | 1–2 дня | 3–5 дней | 1–2 недели |
Docker на macOS работает через Linux VM (Colima/OrbStack), что добавляет ~2–4 GB RAM overhead. OrbStack значительно оптимальнее Docker Desktop. Для PM2-подхода на Mac Mini Docker не нужен.
4. PM2 → Docker → K3s
Этап 1: PM2 (10–80 юзеров)
PM2 — менеджер процессов для Node.js. Нулевой overhead, встроенный мониторинг, автоперезапуск. Идеален для Mac Mini.
| Критерий | PM2 | Docker | K3s |
|---|---|---|---|
| Простота | ⭐⭐⭐ | ⭐⭐ | ⭐ |
| Изоляция | ❌ Нет (shared FS) | ✅ Полная | ✅ Полная |
| Overhead / процесс | ~0 MB | ~10–20 MB | ~20–30 MB |
| Мониторинг | Встроенный | Отдельно | Prometheus |
| Автоперезапуск | ✅ | ✅ | ✅ + self-healing |
| Динамическое создание | pm2 start | docker run | kubectl apply |
| Горизонтальное масштабирование | ❌ Один хост | ⚠️ Swarm | ✅ Кластер |
| Scale-to-zero | ❌ | ✅ (скриптом) | ✅ (KEDA) |
PM2 — это MVP за 1 день. Создаёте ecosystem-файл, который динамически генерируется из списка юзеров, и запускаете. Нет overhead на контейнеризацию, нет Linux VM на Mac. Минус один: нулевая изоляция. Юзер A теоретически может получить доступ к файлам юзера B (если агент не ограничен через tool policy). Для первых 50 платных юзеров — приемлемо, если настроить tool policy правильно.
Этап 2: Docker (50–200 юзеров)
Когда изоляция становится критичной (или вы переезжаете на VPS), Docker — естественный следующий шаг.
# Dockerfile для OpenClaw-инстанса FROM node:22-alpine RUN npm install -g openclaw WORKDIR /workspace COPY openclaw.json . COPY AGENTS.md . EXPOSE 3000 CMD ["openclaw", "gateway", "start"]
Каждый юзер — отдельный контейнер. Docker Compose управляет всеми контейнерами на одном хосте. При необходимости — Docker Swarm для multi-host.
Этап 3: K3s (200–500+ юзеров)
K3s — облегчённый Kubernetes, идеальный для edge-деплоя. Потребляет ~512 MB RAM (vs ~2 GB у полного K8s). Развёртывается за минуты.
helm install oc-user-123 ./openclaw-chart| Масштаб | Инструмент | Инфра | Стоимость/мес |
|---|---|---|---|
| 10 юзеров | PM2 | Mac Mini | ~₽500 (электричество) |
| 50 юзеров | PM2 / Docker | Mac Mini или 1 VPS | ₽500–720 |
| 100 юзеров | Docker Compose | 2 VPS (Hetzner CX32) | €14 (~₽1480) |
| 200 юзеров | Docker Swarm / K3s | 3 VPS (CX42) | €41 (~₽4340) |
| 500 юзеров | K3s | 5–7 VPS (CX42) | €68–95 (~₽7200–10000) |
5. Unit Economics
Стоимость LLM API на одного юзера
Типичный юзер: 20–50 сообщений/день → ~100K input + ~50K output tokens/день.
| Модель (февраль 2026) | Input $/1M tok | Output $/1M tok | $/юзер/день | $/юзер/мес |
|---|---|---|---|---|
| DeepSeek V3.2 (cache hit) | $0.028 | $0.14 | $0.010 | $0.30 |
| DeepSeek V3.2 (cache miss) | $0.28 | $1.10 | $0.083 | $2.50 |
| Gemini 3 Flash | $0.075 | $0.30 | $0.023 | $0.69 |
| Kimi K2.5 | $0.60 | $2.00 | $0.160 | $4.80 |
| GPT-5.3 | $0.15 | $0.60 | $0.045 | $1.35 |
| Claude Opus 4.6 | $5.00 | $25.00 | $1.750 | $52.50 |
DeepSeek V3.2 с prompt caching — $0.30/юзер/мес. Или Gemini 3 Flash — $0.69/юзер/мес. Claude Opus 4.6 — только для premium-тарифа ($52/юзер!).
Полная себестоимость: сводная таблица
| Статья расходов | 10 юзеров | 50 юзеров | 100 юзеров | 500 юзеров |
|---|---|---|---|---|
| Инфра (VPS/Mac) | ₽500 | ₽720 | ₽1 480 | ₽7 200 |
| LLM API (DeepSeek V3.2) | ₽300 | ₽1 500 | ₽3 000 | ₽15 000 |
| Домен + SSL | ₽100 | ₽100 | ₽100 | ₽100 |
| Бэкапы (S3) | ₽0 | ₽100 | ₽200 | ₽1 000 |
| Мониторинг | ₽0 | ₽0 | ₽0 | ₽400 |
| Итого / мес | ₽900 | ₽2 420 | ₽4 780 | ₽23 700 |
| На юзера / мес | ₽90 | ₽48 | ₽48 | ₽47 |
Маржинальность по тарифам
| Тариф | Цена/мес | Себестоимость | Маржа | Маржа % |
|---|---|---|---|---|
| Free (ограничен) | ₽0 | ₽48 | −₽48 | — |
| Basic | ₽499 | ₽48 | ₽451 | 90% |
| Pro | ₽1 499 | ₽48 | ₽1 451 | 97% |
| Enterprise | ₽4 999 | ₽48 | ₽4 951 | 99% |
При цене ₽499/мес и себестоимости ₽48/юзер маржа составляет 90%. 100 платных юзеров = ₽49 900 MRR при расходах ₽4 780. Чистая прибыль — ₽45 120/мес уже на 100 юзерах.
6. Scale-to-zero
Идея: контейнер спит, пока не придёт сообщение
Если юзер не пишет 30 минут — зачем его контейнеру потреблять ресурсы? Scale-to-zero означает: контейнер останавливается при бездействии и просыпается при входящем webhook.
Числа
| Метрика | Без scale-to-zero | Со scale-to-zero |
|---|---|---|
| 100 юзеров: одновременно запущенных | 100 | ~20 (20% активны) |
| RAM потребление | ~8.4 GB | ~3.6 GB |
| Нужный VPS | Hetzner CX42 (16 GB) | Hetzner CX32 (8 GB) |
| Стоимость инфры | €13.60/мес | €6.80/мес |
| Стоимость/юзер | €0.136 | €0.068 |
| Cold start задержка | 0 сек | 3–15 сек |
Экономия: 2× на инфраструктуре. Но есть цена — cold start 3–15 секунд при первом сообщении после idle. OpenClaw при старте загружает workspace, память и LLM-контекст.
Решение проблемы cold start
# Скрипт усыпления неактивных контейнеров (cron каждые 5 минут)
#!/bin/bash
IDLE_THRESHOLD=1800 # 30 минут в секундах
for container in $(docker ps --filter "label=openclaw" -q); do
USER_ID=$(docker inspect --format '{{index .Config.Labels "user_id"}}' $container)
LAST_ACTIVITY=$(cat /data/users/$USER_ID/last_activity 2>/dev/null || echo 0)
NOW=$(date +%s)
IDLE=$((NOW - LAST_ACTIVITY))
if [ $IDLE -gt $IDLE_THRESHOLD ]; then
echo "Stopping idle container for user $USER_ID (idle ${IDLE}s)"
docker stop $container
fi
done
7. Webhook Router
Зачем нужен
Telegram отправляет webhook на один URL. У нас 500 ботов = 500 разных токенов. Router — это «почтальон», который смотрит на токен в URL и доставляет запрос нужному процессу.
Реализация на Node.js (~40 строк)
const http = require('http');
const httpProxy = require('http-proxy');
const { exec } = require('child_process');
const proxy = httpProxy.createProxyServer();
const routes = new Map(); // bot_token → { port, userId }
// Загружаем маршруты из users.json
function loadRoutes() {
const users = require('/data/users.json');
routes.clear();
users.forEach(u => routes.set(u.botToken, {
port: u.port,
userId: u.id
}));
}
loadRoutes();
// Обновляем маршруты по SIGHUP
process.on('SIGHUP', loadRoutes);
http.createServer(async (req, res) => {
// URL: /webhook/{bot_token}
const match = req.url.match(/^\/webhook\/(.+)/);
if (!match) { res.writeHead(404); return res.end('Not found'); }
const token = match[1];
const route = routes.get(token);
if (!route) { res.writeHead(404); return res.end('Bot not found'); }
// Обновляем last_activity для scale-to-zero
const fs = require('fs');
fs.writeFileSync(`/data/users/${route.userId}/last_activity`,
String(Math.floor(Date.now() / 1000)));
// TODO: проверить, жив ли контейнер, если нет — запустить
proxy.web(req, res, {
target: `http://127.0.0.1:${route.port}`
});
}).listen(8080, () => console.log('Webhook router on :8080'));
Вариант на Nginx + Lua (высокая производительность)
# nginx.conf (с OpenResty / lua-nginx-module)
http {
lua_shared_dict routes 10m;
init_by_lua_block {
-- Загрузить маршруты из файла при старте
local cjson = require "cjson"
local f = io.open("/data/routes.json", "r")
local users = cjson.decode(f:read("*a"))
f:close()
for _, u in ipairs(users) do
ngx.shared.routes:set(u.bot_token, u.port)
end
}
server {
listen 8080;
location ~ ^/webhook/(.+)$ {
set $bot_token $1;
set $backend_port '';
access_by_lua_block {
local port = ngx.shared.routes:get(ngx.var.bot_token)
if not port then
ngx.exit(404)
end
ngx.var.backend_port = port
}
proxy_pass http://127.0.0.1:$backend_port;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
Nginx + Lua — для production с 200+ юзеров. Обрабатывает десятки тысяч req/sec. Node.js-вариант — для MVP, проще в разработке.
8. Примеры конфигураций
PM2 ecosystem (динамическая генерация)
// generate-ecosystem.js
const users = require('/data/users.json');
module.exports = {
apps: users.map(u => ({
name: `oc-${u.id}`,
script: 'openclaw',
args: `gateway start --config /data/users/${u.id}/openclaw.json`,
cwd: `/data/users/${u.id}`,
max_memory_restart: '250M',
autorestart: true,
env: {
NODE_ENV: 'production',
PORT: u.port,
}
}))
};
// Использование:
// node generate-ecosystem.js > ecosystem.config.js
// pm2 start ecosystem.config.js
Docker Compose (для 3 юзеров — шаблон)
# docker-compose.yml
version: "3.8"
services:
router:
build: ./router
ports:
- "8080:8080"
volumes:
- ./data/users.json:/data/users.json:ro
restart: always
oc-user-001:
image: openclaw-instance
volumes:
- ./data/users/001:/workspace
environment:
- PORT=3000
expose:
- "3000"
labels:
openclaw: "true"
user_id: "001"
mem_limit: 256m
restart: unless-stopped
oc-user-002:
image: openclaw-instance
volumes:
- ./data/users/002:/workspace
environment:
- PORT=3000
expose:
- "3000"
labels:
openclaw: "true"
user_id: "002"
mem_limit: 256m
restart: unless-stopped
# Для 500 юзеров: генерировать этот файл скриптом
# python generate_compose.py > docker-compose.yml
Скрипт provisioning нового юзера
#!/bin/bash
# provision-user.sh — создание нового пользователя SaaS
set -e
USER_ID=$1
BOT_TOKEN=$2
PORT=$3
if [ -z "$USER_ID" ] || [ -z "$BOT_TOKEN" ] || [ -z "$PORT" ]; then
echo "Usage: ./provision-user.sh <user_id> <bot_token> <port>"
exit 1
fi
USER_DIR="/data/users/$USER_ID"
# 1. Создать директорию
mkdir -p "$USER_DIR/workspace/memory"
# 2. Сгенерировать openclaw.json
cat > "$USER_DIR/openclaw.json" <<EOF
{
"channels": {
"telegram": {
"botToken": "$BOT_TOKEN",
"dmPolicy": "open"
}
},
"agents": {
"defaults": {
"model": { "primary": "deepseek/deepseek-chat" },
"maxConcurrent": 4,
"contextTokens": 100000
}
},
"gateway": {
"port": $PORT
}
}
EOF
# 3. Скопировать шаблон AGENTS.md
cp /data/templates/AGENTS.md "$USER_DIR/workspace/"
# 4. Установить webhook в Telegram
WEBHOOK_URL="https://api.your-saas.com/webhook/$BOT_TOKEN"
curl -s "https://api.telegram.org/bot$BOT_TOKEN/setWebhook?url=$WEBHOOK_URL"
# 5. Запустить процесс (PM2-вариант)
pm2 start openclaw --name "oc-$USER_ID" \
-- gateway start --config "$USER_DIR/openclaw.json"
# 6. Добавить маршрут в router
python3 /data/scripts/add_route.py "$BOT_TOKEN" "$PORT" "$USER_ID"
echo "✅ User $USER_ID provisioned on port $PORT"
Шаблон openclaw.json для SaaS
{
"channels": {
"telegram": {
"botToken": "{{BOT_TOKEN}}",
"dmPolicy": "open"
}
},
"agents": {
"defaults": {
"model": {
"primary": "deepseek/deepseek-chat",
"fallback": "google/gemini-2.5-flash"
},
"maxConcurrent": 4,
"contextTokens": 100000
},
"list": [
{
"id": "main",
"default": true,
"workspace": "/workspace",
"tools": {
"allow": ["read", "web_search", "session_status",
"tts", "image"],
"deny": ["exec", "process", "write", "edit",
"browser", "nodes", "canvas"]
}
}
]
},
"session": {
"dmScope": "main",
"reset": { "mode": "idle", "idleMinutes": 120 }
},
"gateway": {
"port": "{{PORT}}"
}
}
9. Узкие места и решения
| Узкое место | Симптом | Решение |
|---|---|---|
| RAM | OOM-killer убивает процессы при >80% RAM | Scale-to-zero + max_memory_restart: 250M в PM2. Или добавить VPS-ноды. |
| Cold start | 3–15 сек задержка при первом сообщении | Keep-alive 30 мин + instant ack + предиктивный прогрев |
| LLM latency | DeepSeek: 3–10 сек, Claude: 10–30 сек | Streaming responses + быстрые модели (Gemini 3 Flash: 1–3 сек) |
| Telegram rate limits | 429 Too Many Requests при массовой отправке | Throttling: max 30 msg/sec per bot, 20 msg/min в группу |
| Disk I/O | Медленное чтение/запись workspace при 500 юзерах | SSD обязателен. NVMe предпочтителен. tmpfs для hot data. |
| Bot creation | BotFather лимитирует создание ботов (20 на аккаунт) | Несколько Telegram-аккаунтов. Или программный BotAPI сервер. |
| Single point of failure | Mac Mini выключился — все 100 юзеров без сервиса | VPS-ноды как failover. Или изначально на VPS. |
| Docker на macOS | 2–4 GB overhead на Linux VM | Использовать PM2 вместо Docker на Mac. Или OrbStack (минимальный overhead). |
Мониторинг: что отслеживать
pm2 monit или Prometheus + cAdvisor для Docker. Алерт при >200 MB idle.10. Пошаговый план запуска MVP
Неделя 1: MVP на Mac Mini
Неделя 2–3: Автоматизация + биллинг
Месяц 2: Docker + первый VPS
Месяц 3–6: Production и масштаб
| Этап | Юзеры | MRR | Расходы | Прибыль |
|---|---|---|---|---|
| MVP (неделя 1) | 10 (тест) | ₽0 | ₽500 | — |
| Launch (месяц 1) | 30 | ₽15 000 | ₽2 000 | ₽13 000 |
| Growth (месяц 3) | 100 | ₽50 000 | ₽5 000 | ₽45 000 |
| Scale (месяц 6) | 500 | ₽250 000 | ₽24 000 | ₽226 000 |
1. Mac Mini M4 Pro потянет 80–100 юзеров — этого хватит на месяцы.
2. Себестоимость ~₽48/юзер/мес при DeepSeek V3.2. Маржа 90% при цене ₽499.
3. PM2 → Docker → K3s — естественная эволюция без переписывания кода.
4. Scale-to-zero даёт 2× экономию на инфраструктуре.
5. MVP запускается за 3–5 дней. Production-ready — за месяц.
6. 500 юзеров × ₽499 = ₽250K MRR при ₽24K расходах. Это работающий бизнес.