Все гонятся за идеальными промптами, MCP и навыками для агентов. А реальный результат определяет то, чем вы их кормите — ваша собственная база знаний.
База знаний
Directus — headless CMS и backend как контентный хаб для мультисайтовой архитектуры
Directus превращает любую SQL-базу в общий бэкенд для людей и ИИ-агентов: единый админ-интерфейс, мгновенные REST и GraphQL API, политики доступа, нативный MCP-сервер и простое развёртывание на собственном VPS.
Когда вокруг вас уже есть пара сайтов, отдельная база знаний и ИИ-агенты, которые должны где-то читать и куда-то писать — очень быстро становится понятно, что им всем нужен один общий бэкенд. Не три плагина к WordPress, не пять копий контента в Notion, а одно место, где лежит правда.
Directus (directus.io) — это open-source headless CMS и backend-as-a-service, который надстраивается поверх вашей SQL-базы и превращает её в такой общий контур. В этом руководстве разберу, как он устроен, зачем нужен для мультисайтовой архитектуры и ИИ-агентов и как развернуть его на своём VPS — даже если терминал для вас пока не родной.
Что такое Directus
Directus — это бесплатная open-source платформа, которая подключается к вашей SQL-базе (PostgreSQL, MySQL, SQLite, MS SQL, OracleDB) и достраивает поверх неё четыре вещи:
- Админку. Удобный веб-интерфейс для редакторов, где можно работать с любыми таблицами как с контент-базой.
- Мгновенные API. REST и GraphQL появляются автоматически — ничего писать не надо.
- Права и политики. Кто что видит, кто что правит, вплоть до отдельных строк и полей.
- Автоматизации и ИИ. Встроенный no-code конструктор Flows и нативный MCP-сервер для агентов.
Главное отличие от классических CMS — Directus не навязывает свою закрытую схему. База данных остаётся вашей, и в любой момент вы можете его отключить и продолжить работать напрямую с PostgreSQL. Это ровно та архитектура, которую я называю «внешний контур»: данные живут в независимом слое, а не внутри конкретного инструмента.
Почему именно контентный хаб, а не очередная CMS
Когда у вас один сайт — вам достаточно любой CMS. Когда их становится больше, начинаются странные вещи. У каждого сайта свой админ, каждому нужна своя авторизация, контент дублируется, агенты не понимают, куда ходить, и в итоге половина времени уходит на то, чтобы синхронизировать всё это вручную.
Контентный хаб решает эту задачу одним движением: один бэкенд, одна база, много витрин.
Один Directus — несколько сайтов
В моей архитектуре один экземпляр Directus может обслуживать сразу:
- pimenov.ai на Astro
- pimenov.ru на Astro или Nuxt
- reload.pimenov.ai — закрытую платформу клуба
- внутренние базы знаний и справочники
- хранилища данных для ИИ-агентов и Telegram-ботов
Каждая витрина ходит в один и тот же API, но видит только свой срез контента — за это отвечают политики доступа, а не отдельные инсталляции.
Один источник для людей и для агентов

flowchart LR
DB[(PostgreSQL)] --> D[Directus]
D -->|REST / GraphQL| S1["pimenov.ai"]
D -->|REST / GraphQL| S2["reload.pimenov.ai"]
D -->|REST / GraphQL| S3["pimenov.ru"]
D -->|MCP| A1["ИИ-агенты контента"]
D -->|Flows| A2["Автоматизации"]
D -->|App| U["Редакторы"]Фронтенд, редактор и агент читают и пишут в одну и ту же базу через один и тот же слой правил. Никакой дублирующей логики, никаких разных источников правды.
Как это выглядит против Strapi и Sanity
Практичное сравнением — на что смотреть, если выбираете headless CMS именно под сценарий «много сайтов и агенты».
| Что сравниваем | Directus | Strapi | Sanity |
| База данных | Любая существующая SQL | Своя SQL-схема | Проприетарный облачный datastore |
| Self-hosted на своём VPS | Да, один Docker-образ | Да | Нет, только Cloud |
| Админка «из коробки» | Полная, без кода | Полная, с настройкой | Studio, требует JS-кода |
| Нативный MCP для ИИ | Да | Нет | Нет |
| Встроенные автоматизации | Flows | Через внешние сервисы | Через Functions |
| Глубина политик | Коллекция + поле + строка | Коллекция + поле | Dataset + роль |
Архитектура контентного хаба
В простом виде всё выглядит так: один VPS, один PostgreSQL, один Directus и несколько логических зон внутри.
flowchart TB
subgraph VPS["VPS (Docker Compose)"]
subgraph DB["PostgreSQL"]
C1["Зона Sites"]
C2["Зона Knowledge"]
C3["Зона Agents"]
end
D["Directus"]
R["Redis (кэш)"]
N["Caddy / Nginx"]
end
D --- DB
D --- R
N --- D
N -->|HTTPS| Web["Сайты и ИИ-агенты"]И четыре логических зоны, которые удобно держать в голове с самого начала:
| Зона | Что хранит | Кто пишет | Кто читает |
| Sites | Страницы, статьи, блоки, медиа сайтов | Редакторы, ИИ-ассистент | Фронтенды, агенты |
| Knowledge | Справочники, методички, канонические документы | Вы, редакторы | ИИ-агенты, чат-боты |
| Agents | Промпты, логи, задачи и результаты агентов | Агенты, оркестратор | Вы, другие агенты |
| Media | Файлы, изображения, аудио, PDF | Редакторы, агенты | Все |
sites и поле-связь на неё, а не через префикс в названии. Так одна политика закрывает весь контент одного сайта от другого и от агентов, которым туда нельзя.Права и политики: зачем это важно для агентов
В Directus используется policy-based access control — политика это набор правил, который прикрепляется к роли или напрямую к пользователю.
Для контентного хаба минимально достаточно пяти ролей:
| Роль | Что может | Кому |
| Admin | Всё | Вам |
| Editor | CRUD по контенту своего сайта | Редакторам |
| Agent-Writer | Создавать черновики статей, читать базу знаний | Агентам-писателям |
| Agent-Reader | Только чтение базы знаний и сайтов | RAG-ботам и чат-агентам |
| Public | Чтение опубликованного контента | Фронтендам |
Правила умеют учитывать значения в строках. Например, редактору pimenov.ai можно разрешить править статьи только своего сайта:
{
"collection": "articles",
"action": "update",
"permissions": { "site": { "_eq": "pimenov.ai" } },
"fields": ["title", "body", "status", "tags"]
}И отдельный важный момент для ИИ-агентов — никогда не давайте одного токена на всех. Для каждого агента заводите отдельного пользователя с узкой ролью и своим статическим токеном. Это даёт три вещи:
- Раздельные логи действий в
directus_activity - Возможность моментально отозвать доступ одному конкретному агенту
- Понятную историю: кто из агентов что и когда изменил
API и интеграция
REST
Каждая коллекция автоматически получает REST-эндпоинт:
# Опубликованные статьи для pimenov.ai
curl "https://concms.pimenov.ai/items/articles?filter[site][_eq]=pimenov.ai&filter[status][_eq]=published&fields=title,slug,body,tags.*" \
-H "Authorization: Bearer $DIRECTUS_TOKEN"GraphQL
То же самое через GraphQL — удобно, когда нужно забирать связанные сущности одним запросом:
query PublishedArticles {
articles(
filter: { site: { _eq: "pimenov.ai" }, status: { _eq: "published" } }
) {
title
slug
body
tags { name }
}
}Astro Content Layer
Для Astro-сайта (как pimenov.ai) Directus подключается как источник через официальный loader:
// src/content.config.ts
import { defineCollection } from 'astro:content';
import { directusLoader } from '@directus/astro';
export const collections = {
articles: defineCollection({
loader: directusLoader({
url: import.meta.env.DIRECTUS_URL,
token: import.meta.env.DIRECTUS_TOKEN,
collection: 'articles',
filter: { site: { _eq: 'pimenov.ai' }, status: { _eq: 'published' } },
}),
}),
};На каждый сайт — свой loader с нужным фильтром. База одна, фильтры разные.
Внутренние ИИ-агенты и ассистенты
Это, пожалуй, самая интересная часть истории. Directus предлагает три уровня работы с ИИ, и их можно спокойно использовать вместе.
AI Assistant прямо в админке
Встроенный помощник в интерфейсе Directus. Умеет писать и переписывать текст в полях, переводить контент между языками, суммировать длинные документы и выполнять простые действия по запросу редактора.
Это инструмент для тех, кто работает руками в админке и хочет быстрого ассистента «под рукой» — без отдельного чат-окна и копи-пасты.
Native MCP Server
Дальше становится интереснее. Directus работает как нативный MCP-сервер — это значит, что любой MCP-клиент (Claude Desktop, Cursor, ваши собственные агенты на OpenAI или Anthropic SDK) может подключиться к нему и получить набор инструментов: чтение, запись, поиск, работа с файлами.
Типичный конвейер для контент-агента:
- Агент «Писатель» подключается к Directus через MCP с токеном роли
Agent-Writer - Читает базу знаний и предыдущие публикации по теме
- Создаёт черновик статьи со статусом
draftи автором = токен агента - Редактор видит черновик в админке, правит, меняет статус на
published - Фронтенд (Astro ISR) подхватывает опубликованную статью и ребилдит страницу
Подключение MCP-клиента на примере Claude Desktop:
{
"mcpServers": {
"directus": {
"command": "npx",
"args": ["-y", "@directus/mcp"],
"env": {
"DIRECTUS_URL": "https://cms.pimenov.ai",
"DIRECTUS_TOKEN": "<токен роли Agent-Writer>"
}
}
}
}Flows — короткие автоматизации как «внутренние агенты»
Flows — встроенный конструктор потоков с триггерами и операциями. Триггеры: изменение данных, расписание, webhook, ручной запуск. Операции: условия, работа с коллекциями, HTTP-запросы, скрипты и вызовы LLM через расширения.
Что удобно собирать на Flows:
- Авто-теггер. При создании статьи LLM расставляет теги по тексту.
- Перевод. При смене языка карточки запускается перевод, результат пишется в соседнее поле.
- Индексация базы знаний. При обновлении документа автоматически пересчитываются чанки и эмбеддинги в
knowledge_chunks(pgvector). - Уведомления. При переводе статьи в статус
reviewбот пишет автору в Telegram.
Grani простая: Flows — для коротких реактивных автоматизаций внутри Directus. Длинные многошаговые сценарии — уже задача внешнего оркестратора, например OpenClaw. Обе системы ходят в одну и ту же базу, но с разными токенами и ролями.
Развёртывание на своём VPS — по шагам
Дальше — практика. Я описываю её так, чтобы можно было пройти по шагам, даже если терминал для вас пока не родной. Все команды выполняются через SSH к вашему VPS: ssh user@ip-адрес-vps.
Что понадобится
- VPS с Ubuntu 22.04+, минимум 2 vCPU / 4 ГБ RAM / 40 ГБ SSD
- Домен (например,
cms.pimenov.ai) с A-записью на IP VPS - Docker и Docker Compose
- Caddy или Nginx перед Directus для HTTPS
Шаг 1. Поставить Docker
# Обновляем список пакетов
sudo apt update
# Ставим Docker и плагин Compose
sudo apt install -y docker.io docker-compose-plugin
# Разрешаем текущему пользователю запускать docker без sudo
sudo usermod -aG docker $USER
# Выходим, чтобы изменения применились
exitПосле повторного ssh проверяем:
docker --version
docker compose versionШаг 2. Создать структуру папок
mkdir -p ~/directus && cd ~/directus
mkdir -p uploads extensions databaseШаг 3. Написать docker-compose.yml
Создайте файл ~/directus/docker-compose.yml со следующим содержимым:
services:
database:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- ./database:/var/lib/postgresql/data
environment:
POSTGRES_USER: directus
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: directus
healthcheck:
test: ["CMD", "pg_isready", "-U", "directus"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
restart: unless-stopped
directus:
image: directus/directus:latest
restart: unless-stopped
ports:
- "127.0.0.1:8055:8055" # слушаем только локально, наружу через Caddy
volumes:
- ./uploads:/directus/uploads
- ./extensions:/directus/extensions
depends_on:
database:
condition: service_healthy
environment:
KEY: ${DIRECTUS_KEY}
SECRET: ${DIRECTUS_SECRET}
DB_CLIENT: pg
DB_HOST: database
DB_PORT: 5432
DB_DATABASE: directus
DB_USER: directus
DB_PASSWORD: ${DB_PASSWORD}
CACHE_ENABLED: "true"
CACHE_STORE: redis
REDIS: redis://cache:6379
ADMIN_EMAIL: ${ADMIN_EMAIL}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
PUBLIC_URL: https://concms.pimenov.aiШаг 4. Заполнить .env
Рядом с docker-compose.yml создаём .env. Секреты генерируются один раз и никуда не коммитятся:
openssl rand -hex 16 # для KEY
openssl rand -hex 32 # для SECRET и DB_PASSWORDПример .env:
DB_PASSWORD=<сгенерированное значение>
DIRECTUS_KEY=<uuid>
DIRECTUS_SECRET=<строка 32+ символов>
ADMIN_EMAIL=sergey@pimenov.am
ADMIN_PASSWORD=<надёжный пароль>.env не должен попадать в git — сразу добавьте его в .gitignore. На сервере ставим права строго для владельца: chmod 600 .env.Шаг 5. Запустить и проверить
# Запускаем все сервисы в фоне
docker compose up -d
# Смотрим логи Directus
docker compose logs -f directusПри первом старте Directus создаст схему и админа. Когда в логах появится строка Server started at http://0.0.0.0:8055 — сервис готов.
Шаг 6. Поднять HTTPS через Caddy
Caddy выдаёт сертификаты Let's Encrypt автоматически — не нужно ни certbot, ни руками писать конфиги SSL.
sudo apt install -y caddy
sudo nano /etc/caddy/CaddyfileСодержимое Caddyfile:
concms.pimenov.ai {
reverse_proxy 127.0.0.1:8055
encode gzip
}sudo systemctl reload caddyЧерез минуту https://concms.pimenov.ai работает с валидным сертификатом.
Шаг 7. Настроить бэкапы
Минимум — ежедневный дамп базы и архив загруженных файлов:
# ~/directus/backup.sh
#!/bin/bash
STAMP=$(date +%Y%m%d-%H%M)
mkdir -p ~/backups
# Дамп PostgreSQL
docker compose exec -T database pg_dump -U directus directus | gzip > ~/backups/db-$STAMP.sql.gz
# Архив медиа
tar -czf ~/backups/uploads-$STAMP.tar.gz -C ~/directus uploads
# Удаляем бэкапы старше 14 дней
find ~/backups -type f -mtime +14 -deleteДобавляем в cron:
crontab -e
# Каждый день в 03:15
15 3 * * * /bin/bash /home/$USER/directus/backup.sh >> /home/$USER/backups/backup.log 2>&1Шаг 8. Обновлять
cd ~/directus
docker compose pull
docker compose up -d
docker compose logs -f directus # проверяем, что миграции прошлиDirectus сам накатывает миграции схемы при запуске. Перед любым мажорным апдейтом — обязательный свежий бэкап.
Типовые сценарии хаба
Мультисайтовая публикация
- В коллекции
sites— записиpimenov.ai,reload.pimenov.ai,pimenov.ru - В
articlesобязательное поле-связьsiteна эту коллекцию - Политика
Publicотдаёт толькоstatus = published - Каждый фронтенд на Astro тянет свой срез по
filter[site][_eq] - Кэш на уровне фронтенда (Astro ISR) и Redis на уровне Directus
База знаний для агентов
- Коллекция
knowledge_docs— канонические документы - Flow на обновление документа режет текст на чанки и считает эмбеддинги в
knowledge_chunksчерез pgvector - Агент-ресёрчер через MCP делает семантический поиск по чанкам и отдаёт релевантные куски в контекст LLM
- Обновление источника = автоматическое обновление индекса. Агент всегда работает с актуальной базой
Конвейер «агент → черновик → публикация»
- Внешний оркестратор (у меня это OpenClaw) получает задачу «написать статью про X»
- Агент через MCP читает базу знаний и предыдущие статьи по теме
- Пишет черновик в
articlesсо статусомdraft, автор — токен агента - Flow отправляет уведомление в Telegram автору
- Автор правит в админке, меняет статус на
published - Webhook запускает ребилд сайта
Чего стоит избегать
- ❌ Один токен на всех агентов — теряете аудит и не можете точечно отозвать доступ
- ❌ Directus наружу без HTTPS и rate-limit — в продакшене обязателен reverse proxy с ограничениями
- ❌ Хранить API-ключи в полях коллекций — для секретов есть
.envи переменные окружения Flows - ❌ Смешивать оперативные данные и контент в одной схеме без политик
- ❌ Разрешать агентам сразу публиковать — статус
draftи человеческая валидация доpublishedэкономят много нервов - ❌ Игнорировать бэкапы — без крона и выноса дампов восстановление после сбоя невозможно
- ❌ Использовать SQLite для контентного хаба в продакшене — для мультисайта и параллельной записи нужен PostgreSQL
Чеклист перед запуском в прод
Ссылки
- Сайт: directus.io
- Документация: docs.directus.io
- MCP-сервер: directus.io/mcp
- Docker-образ: hub.docker.com/r/directus/directus
- Astro-интеграция: docs.directus.io/guides/astro
- GitHub: github.com/directus/directus
По теме
- Статья: Как сайт становится живым контуром: человек, агент, Notion и live-публикация
- Блог: Быстрые деньги в ИИ — это ловушка. Настоящие — в вашей экспертизе
- База знаний: Strapi — open-source Headless CMS для тех, кто хочет владеть своими данными
Если вы уже чувствуете, что один сайт и один Notion перестают вмещать всё, что вы делаете, — такой контентный хаб обычно проще спроектировать один раз аккуратно, чем потом вытаскивать контент из пяти разных систем. Если хочется разобрать именно вашу конфигурацию — напишите, посмотрим вместе.
flowchart LR DB[(PostgreSQL)] --> D[Directus] D -->|REST / GraphQL| S1["pimenov.ai"] D -->|REST / GraphQL| S2["reload.pimenov.ai"] D -->|REST / GraphQL| S3["pimenov.ru"] D -->|MCP| A1["ИИ-агенты контента"] D -->|Flows| A2["Автоматизации"] D -->|App| U["Редакторы"]