MAATRIX / Блог / Directus на сервере: частые ошибки и решения

Directus на сервере: частые ошибки и решения

MAATRIX

Directus — не обычная CMS вроде Strapi, которая создаёт свою базу данных с нуля. Это headless-обёртка поверх уже существующей SQL-базы: она подключается к вашим таблицам, читает их структуру и превращает в API и админку без миграции данных. Именно эта особенность и рождает большинство проблем на проде — от «коллекции не отображаются» до путаницы с правами доступа. Собрал здесь конкретные ошибки, с которыми реально сталкиваешься при развёртывании Directus на VPS, и рабочие решения.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Directus как обёртка над существующей БД: в чём подвох

Ключевое отличие Directus от Strapi, WordPress или любой другой CMS с собственной схемой — он не владеет вашими данными. Вы можете указать ему на боевую PostgreSQL-базу интернет-магазина, которая работает уже три года, и Directus подключится, прочитает существующие таблицы products, orders, customers и сразу даст по ним API и админку. Никакой миграции, никакого экспорта-импорта.

Для этого Directus добавляет в вашу базу служебные таблицы с префиксом directus_ — там хранятся коллекции, поля, права доступа, пользователи панели, ревизии. Ваши бизнес-таблицы он не трогает и не меняет их структуру без явной команды через Data Studio или CLI.

Отсюда и главный источник путаницы: Directus не знает о таблице, пока вы не скажете ему «управляй этой таблицей» через раздел Settings → Data Model, либо через directus schema apply. Просто существование таблицы в БД не делает её видимой коллекцией — это осознанное архитектурное решение, а не баг, но новичков оно ловит регулярно.

Практическое следствие: перед развёртыванием на проде решите заранее, будет ли Directus единственным писателем в базу, или к тем же таблицам будут писать другие сервисы (например, старый PHP-бэкенд). Второй сценарий рабочий, но требует аккуратности с кэшем Directus — об этом ниже.

Ошибки подключения к базе данных

Самая частая проблема при первом запуске — Directus падает сразу после старта с ошибкой вида:

Error: connect ECONNREFUSED 127.0.0.1:5432

или

KnexTimeoutError: Knex: Timeout acquiring a connection

Проверьте переменные окружения. Directus использует Knex как прослойку к БД, и для PostgreSQL минимальный набор в .env выглядит так:

DB_CLIENT=pg
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=mydb
DB_USER=directus_user
DB_PASSWORD=strong_password_here

Если PostgreSQL и Directus в разных Docker-контейнерах на одной docker-compose сети, DB_HOST должен быть именем сервиса из compose-файла (db), а не 127.0.0.1 — это классическая ошибка при переносе с локальной разработки на сервер.

Проверьте права SQL-пользователя. Directus должен уметь не только читать и писать данные, но и создавать/менять таблицы directus_* при первом запуске и обновлениях:

CREATE USER directus_user WITH PASSWORD 'strong_password_here';
GRANT ALL PRIVILEGES ON DATABASE mydb TO directus_user;
\c mydb
GRANT ALL ON SCHEMA public TO directus_user;

Без последней строки на PostgreSQL 15+ (где схема public по умолчанию не даёт прав CREATE всем подряд) Directus падает при попытке создать служебные таблицы с ошибкой permission denied for schema public.

Таймауты пула соединений. Если БД доступна, но соединение обрывается под нагрузкой, увеличьте пул и таймауты:

DB_POOL__MIN=2
DB_POOL__MAX=10
DB_CONNECTION_TIMEOUT=30000

На небольшом VPS (2-4 ГБ RAM) не ставьте DB_POOL__MAX выше 10-15 — PostgreSQL сам ограничен max_connections, и если у вас несколько сервисов делят одну базу, легко упереться в лимит соединений на стороне сервера, а не Directus. Более глубокая настройка самой PostgreSQL под нагрузку — отдельная тема, разобрана в статье про частые ошибки PostgreSQL на сервере.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Коллекции не отображаются или рассинхронизированы

Развернули Directus на новом сервере, база та же — а коллекций в интерфейсе нет или показаны не все. Это связано с тем, как Directus хранит метаданные схемы.

Проверьте таблицу directus_collections. Именно она определяет, какие таблицы базы видны как коллекции:

SELECT collection, icon, singleton FROM directus_collections;

Если ваша таблица там не значится — Directus про неё не знает, даже если данные физически есть в БД. Добавить её можно через UI (Settings → Data Model → Create Collection → выбрать существующую таблицу) или через CLI.

Синхронизация схемы между окружениями (staging → prod) — это отдельная механика, и именно здесь чаще всего теряют изменения при деплое:

# на staging: сохранить снапшот схемы в файл
npx directus schema snapshot ./snapshot.yaml

# на проде: посмотреть разницу перед применением
npx directus schema diff ./snapshot.yaml

# применить изменения
npx directus schema apply ./snapshot.yaml

Частая ошибка — деплоить только код и .env, забывая про снапшот схемы. Новая коллекция, добавленная на staging через UI, физически не появится на проде, пока вы явно не примените snapshot. Держите файл снапшота в git рядом с кодом и прогоняйте schema apply в CI/CD-пайплайне как отдельный шаг после миграции.

Если коллекции пропали после обновления версии Directus, проверьте лог на ошибки миграции при старте — Directus выполняет собственные внутренние миграции directus_* таблиц при апгрейде минорной версии, и если процесс был прерван (например, контейнер убит по OOM во время миграции), схема останется в промежуточном состоянии. Восстанавливать вручную опасно — надёжнее поднять БД из бэкапа, сделанного перед апгрейдом, и повторить обновление на чистом снапшоте.

Белый экран, 502 и проблемы с reverse proxy

Локально npx directus start работает, а на проде за nginx — белый экран в браузере или админка зависает на загрузке.

Проверьте PUBLIC_URL. Directus использует эту переменную, чтобы генерировать абсолютные ссылки на ассеты, вебхуки и OAuth redirect:

PUBLIC_URL=https://cms.example.com

Если оставить значение по умолчанию (http://localhost:8055) на проде, админка попытается подгружать статику по localhost из браузера пользователя — отсюда белый экран с ошибками 404 в консоли на JS/CSS-файлы.

Минимальный рабочий nginx-конфиг для проксирования на Directus (по умолчанию слушает порт 8055). Сертификат для этого конфига получите заранее — как это сделать, описано в статье про установку Let's Encrypt SSL на VPS:

server {
    listen 443 ssl http2;
    server_name cms.example.com;

    ssl_certificate     /etc/letsencrypt/live/cms.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cms.example.com/privkey.pem;

    client_max_body_size 100M;

    location / {
        proxy_pass http://127.0.0.1:8055;
        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;
        proxy_read_timeout 300s;
    }
}

client_max_body_size важен отдельно — без него загрузка файлов через Data Studio будет упираться в лимит nginx по умолчанию (1 МБ) и падать с 413 Payload Too Large ещё до того, как запрос дойдёт до Directus.

CORS при отдельном фронтенде. Если вызываете API Directus с другого домена (SPA на Vue/React), включите и настройте CORS явно:

CORS_ENABLED=true
CORS_ORIGIN=https://app.example.com
CORS_CREDENTIALS=true

Оставленный по умолчанию CORS_ORIGIN=false молча блокирует все кросс-доменные запросы — в консоли браузера будет стандартная CORS-ошибка, а не что-то специфичное для Directus, что сбивает с толку при отладке.

Права доступа: 403 вместо данных

Директус завёл встроенную ролевую модель отдельно от прав самой БД, и это второй частый источник непонятных ошибок — 403 Forbidden при том, что данные в базе точно есть.

Публичная роль ограничена по умолчанию. Если вы дёргаете API без токена (анонимный доступ для сайта-витрины), а получаете FORBIDDEN, проверьте права роли Public в Settings → Roles & Permissions → Public — по умолчанию она не имеет доступа ни к одной коллекции, это осознанная защита от случайной утечки данных.

Различайте permissions на уровне поля и на уровне коллекции. Directus позволяет разрешить чтение коллекции, но скрыть отдельные поля (например, email клиента) для конкретной роли — если API возвращает объект без ожидаемого поля вместо ошибки, это почти всегда field-level permission, а не баг.

Проверка через API токен для отладки. Создайте статический токен у сервисного пользователя с нужной ролью и тестируйте запросы напрямую, исключая фронтенд из уравнения:

curl -H "Authorization: Bearer YOUR_STATIC_TOKEN" \
  https://cms.example.com/items/products?limit=5

Если такой запрос тоже возвращает 403 — проблема точно в правах роли, а не в токене на фронтенде или в куках сессии.

Кастомные политики через фильтры. Directus позволяет ограничивать доступ не только по коллекциям, но и по значению поля (row-level permissions) — например, «менеджер видит только заказы своего региона». Такие правила легко упускаются из виду при копировании прав между окружениями — переносите их вместе со snapshot схемы, а не вручную кликая в UI на проде.

Файлы, хранилище и производительность

Directus по умолчанию хранит загруженные файлы локально на диске сервера, и это первое, что стоит пересмотреть перед продакшен-нагрузкой.

Локальное хранилище не переживает горизонтальное масштабирование. Если планируете запускать несколько инстансов Directus за балансировщиком, локальные файлы на диске каждого контейнера рассинхронизируются. Переключитесь на S3-совместимое хранилище:

STORAGE_LOCATIONS=s3
STORAGE_S3_DRIVER=s3
STORAGE_S3_KEY=your_key
STORAGE_S3_SECRET=your_secret
STORAGE_S3_BUCKET=cms-assets
STORAGE_S3_REGION=eu-central-1
STORAGE_S3_ENDPOINT=https://s3.example-provider.com

Это работает с любым S3-совместимым провайдером, не только AWS.

Трансформации изображений грузят CPU. Directus умеет отдавать ресайзнутые превью «на лету» (/assets/uuid?width=300), но при большом трафике это создаёт заметную нагрузку на CPU сервера — каждая уникальная комбинация параметров пересчитывается заново, если не задать заранее известные пресеты:

ASSETS_TRANSFORM_MAX_CONCURRENT=4
ASSETS_TRANSFORM_MAX_OPERATIONS=5

Для тяжёлого трафика с изображениями разумнее поставить перед Directus CDN с кэшированием превью, а не рассчитывать, что каждая трансформация будет пересчитываться сервером заново.

WebSockets для realtime. Если используете подписки Directus (real-time обновления в интерфейсе или через API), включите явно и не забудьте прокинуть апгрейд соединения через nginx:

WEBSOCKETS_ENABLED=true
location /websocket {
    proxy_pass http://127.0.0.1:8055;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Без блока Upgrade/Connection в nginx WebSocket-соединение будет молча падать до обычного HTTP-запроса, и realtime-функции не заработают, хотя обычное API продолжит отвечать нормально — отладка такого сценария обычно занимает больше времени, чем сама настройка.

Ресурсы под Directus на практике: для среднего проекта с несколькими десятками коллекций и умеренным трафиком хватает 2 vCPU и 4 ГБ RAM, где отдельно стоит выделить память под PostgreSQL — сама Node.js-часть Directus довольно лёгкая, основная нагрузка приходится на СУБД при сложных фильтрах и джойнах. Точные цифры зависят от объёма данных и профиля запросов, поэтому воспринимайте это как ориентир для старта, а не готовый sizing.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Нужно ли переносить данные в Directus перед началом работы?

Нет, это и есть его основное отличие от других CMS — он подключается к существующей SQL-базе и работает с данными как есть, без миграции и без создания дублирующей схемы.

Можно ли использовать Directus с базой, в которую параллельно пишет другой сервис?

Да, это поддерживаемый сценарий, но учитывайте кэш: если Directus кэширует ответы API (CACHE_ENABLED=true), сторонние изменения в БД могут не отражаться сразу — либо отключайте кэш для таких таблиц, либо явно инвалидируйте его через вебхук стороннего сервиса.

Почему после docker compose up контейнер Directus перезапускается по кругу?

Чаще всего это гонка старта: контейнер Directus поднимается раньше, чем PostgreSQL готов принимать соединения. Добавьте depends_on с condition: service_healthy и healthcheck для базы данных в compose-файле.

Как быстро проверить, что дело именно в правах, а не в подключении к БД?

Залогиньтесь под ролью Administrator напрямую в Data Studio — если данные видны там, но не через публичный API, проблема в правах роли, а не в подключении или схеме.

Обязательно ли использовать S3 для файлов, если сервер один и без масштабирования?

Нет, локальное хранилище (STORAGE_LOCATIONS=local) вполне рабочий вариант для одного сервера — просто не забывайте включать директорию с загрузками в регулярный бэкап отдельно от дампа базы данных, как описано в статье про резервное копирование БД на сервере.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →