Зачем собирать весь AI-стек в одном Docker Compose
Для практической AI-автоматизации редко достаточно одного n8n. Нужна связка из нескольких сервисов: оркестратор воркфлоу (n8n), локальная LLM (Ollama) и векторная база для RAG (Qdrant). Запускать их отдельными командами неудобно — теряются зависимости запуска, общая сеть, единый бэкап. Правильный подход — один docker-compose.yml, который поднимает весь стек одной командой.
Эта связка особенно ценна для российского бизнеса: данные не покидают сервер, нет зависимости от OpenAI API, нет платы за токены — всё работает локально и подходит под требования ФЗ-152.
Архитектура self-hosted AI-стека
- n8n — визуальный оркестратор: принимает вебхуки, запускает воркфлоу, вызывает остальные сервисы
- Ollama — локальный LLM-сервер (Qwen, Llama, DeepSeek) с REST API в формате OpenAI
- Qdrant — векторная база данных для семантического поиска и RAG
- PostgreSQL — основная база данных n8n (вместо SQLite для production)
Все сервисы общаются внутри одной Docker-сети по именам контейнеров — никаких портов не нужно открывать наружу, кроме n8n (через nginx).
Полный docker-compose.yml
version: "3.8"
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: n8n
POSTGRES_USER: n8n_user
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./data/postgres:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n_user"]
interval: 10s
retries: 5
ollama:
image: ollama/ollama
volumes:
- ./data/ollama:/root/.ollama
restart: unless-stopped
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
# Удалите блок deploy выше, если нет GPU — будет работать на CPU
qdrant:
image: qdrant/qdrant
volumes:
- ./data/qdrant:/qdrant/storage
restart: unless-stopped
n8n:
image: n8nio/n8n:latest
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n_user
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_HOST=${N8N_HOST}
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://${N8N_HOST}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
# Внутри n8n обращайтесь к сервисам по именам контейнеров:
# Ollama: http://ollama:11434
# Qdrant: http://qdrant:6333
volumes:
- ./data/n8n:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
ollama:
condition: service_started
qdrant:
condition: service_started
ports:
- "5678:5678"
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/n8n.conf:/etc/nginx/conf.d/default.conf
- /etc/letsencrypt:/etc/letsencrypt:ro
depends_on:
- n8n
restart: unless-stopped
Запуск стека
# .env файл рядом с docker-compose.yml
echo "POSTGRES_PASSWORD=$(openssl rand -hex 16)" >> .env
echo "N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env
echo "N8N_HOST=n8n.вашдомен.ru" >> .env
# Поднять весь стек одной командой
docker compose up -d
# Проверить статус всех сервисов
docker compose ps
# Скачать модель в Ollama (после старта контейнера)
docker exec -it $(docker compose ps -q ollama) ollama pull qwen3:8b
Настройка n8n для работы с Ollama и Qdrant внутри сети
Главное преимущество единого docker-compose.yml — сервисы видят друг друга по имени контейнера, без localhost и портов наружу:
- Ollama Credential в n8n: Base URL —
http://ollama:11434(не localhost!) - Qdrant через HTTP Request: эндпоинт —
http://qdrant:6333/collections/мои_документы/points/search
Пример воркфлоу RAG целиком внутри стека: документ → embeddings через Ollama (модель nomic-embed-text) → сохранение вектора в Qdrant → при вопросе пользователя — поиск похожих векторов в Qdrant → передача найденного контекста в LLM-ноду Ollama → ответ.
Минимальные требования к серверу для полного стека
| Компонент | RAM | Диск |
|---|---|---|
| n8n + PostgreSQL | 1–2 GB | 10 GB |
| Qdrant (до 1М векторов) | 1–2 GB | 5–20 GB |
| Ollama + модель 8B | 8–10 GB | 10 GB на модель |
| Итого минимум | 12–16 GB | 40–60 GB |
На сервере с GPU (рекомендуется для Ollama) скорость генерации вырастает в 5–10 раз. Без GPU на CPU стек тоже работает, но ответы LLM будут генерироваться заметно медленнее (десятки секунд на ответ вместо секунд).
Бэкап всего стека одной командой
#!/bin/bash
# backup-stack.sh
DATE=$(date +%Y%m%d_%H%M)
mkdir -p /backups/$DATE
docker exec postgres pg_dump -U n8n_user n8n | gzip > /backups/$DATE/n8n_db.sql.gz
tar -czf /backups/$DATE/qdrant_data.tar.gz ./data/qdrant
tar -czf /backups/$DATE/n8n_data.tar.gz ./data/n8n
echo "Backup completed: /backups/$DATE"
Модели Ollama бэкапить не нужно — они скачиваются заново командой ollama pull за несколько минут, не имеет смысла хранить их копии.
Почему это лучше, чем облачные API
| Параметр | OpenAI/Claude API | Self-hosted стек |
|---|---|---|
| Стоимость при росте объёма | Растёт линейно с токенами | Фиксированная (только сервер) |
| Данные клиентов | Уходят за рубеж | Остаются на вашем сервере |
| Соответствие ФЗ-152 | Нет | Да (при сервере в РФ) |
| Задержка ответа | Зависит от интернета | Зависит только от вашего железа |
| Качество модели | Выше (GPT-4o, Claude) | Близкое для большинства задач (Qwen 3) |
Часто задаваемые вопросы
Можно ли запустить весь AI-стек без GPU?
Да, удалите блок deploy/resources из секции ollama в docker-compose.yml — Ollama автоматически переключится на CPU-инференс. Для моделей 7-8B на CPU это будет работать, но медленнее: 5-15 токенов в секунду вместо 50-100 на GPU.
Как n8n обращается к Ollama и Qdrant в одном Docker Compose?
Все сервисы в одном docker-compose.yml автоматически попадают в общую Docker-сеть и видят друг друга по имени сервиса вместо localhost. Используйте http://ollama:11434 для Ollama и http://qdrant:6333 для Qdrant — порты наружу открывать не нужно.
Сколько стоит содержать такой self-hosted AI-стек?
VPS с 16 GB RAM и GPU обходится от 3000-5000 рублей в месяц у российских провайдеров (Selectel, Timeweb). Без GPU, только на CPU — от 1500-2000 рублей за 16 GB RAM. Это разовая фиксированная стоимость независимо от объёма запросов, в отличие от платы за токены облачных API.