MAATRIX / Блог / BookStack в Docker Compose: готовый файл

BookStack в Docker Compose: готовый файл

MAATRIX

Документация компании обычно расползается по трём местам сразу: часть в Google Docs, часть в чатах, часть в голове у одного сотрудника, который вот-вот уйдёт в отпуск. Confluence решает проблему, но стоит денег за каждого пользователя и требует отдельной инфраструктуры; Notion — красивый, но данные лежат на чужих серверах, а поиск по большому объёму текста работает не всегда быстро. BookStack — практичная середина: понятная структура «книга → глава → страница», простой WYSIWYG-редактор, который не пугает нетехнических сотрудников, и всё это разворачивается одним docker-compose.yml на своём сервере. Ниже — рабочий файл и разбор нюансов, которые на голом конфиге не видны.

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

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

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

Почему BookStack, а не Wiki.js или Confluence

BookStack написан на PHP (Laravel) с MySQL/MariaDB в качестве хранилища — стек скучный и предсказуемый, что для базы знаний скорее плюс: меньше шансов, что через два года проект внезапно сменит архитектуру или бросит поддержку.

Ключевая идея BookStack — жёсткая иерархия контента, а не плоский список страниц, как в большинстве вики-движков:

  • Полки (Shelves) — верхний уровень, группируют книги по темам (например, «Отдел разработки», «HR», «Инфраструктура»).
  • Книги (Books) — соответствуют одному продукту, процессу или разделу знаний.
  • Главы (Chapters) — опциональный промежуточный уровень внутри книги.
  • Страницы (Pages) — сам контент, с WYSIWYG или markdown-редактором на выбор.

Такая структура снижает хаос, привычный для плоских вики: сотрудник, который ищет регламент по отпускам, идёт в книгу «HR», а не гуглит по всей базе в надежде, что нужная страница вообще существует.

Сравнение с ближайшими альтернативами:

BookStackWiki.jsConfluence
ЛицензияMIT, self-hostedAGPL, self-hostedпроприетарная, SaaS/self-hosted
ХранилищеMySQL/MariaDBPostgreSQL, MySQL, SQLiteсвоя закрытая БД
Структура контентакнига → глава → страницаплоский список + тегипространство → страница
РедакторWYSIWYG (TinyMCE) + markdownmarkdown, WYSIWYG, AsciiDocWYSIWYG
Права доступапо ролям, вплоть до отдельной страницыпо ролямпо пространствам
Стоимостьбесплатнобесплатноот $$ за пользователя

Wiki.js гибче по редакторам и хранилищу, но его плоская модель с тегами хуже масштабируется, когда книг становится много и нужна навигация «сверху вниз». Confluence удобнее для больших организаций с готовыми интеграциями (Jira и т.д.), но каждый дополнительный пользователь — это статья расходов, которой на self-hosted BookStack просто нет.

Требования к серверу

BookStack — не тяжёлое приложение, но и не игрушка: PHP-FPM плюс MySQL съедают заметно больше, чем условный статический сайт.

Размер командыvCPURAMДиск
до 15 человек12 ГБ20 ГБ
15-50 человек24 ГБ40 ГБ
50+ человек, много вложений2-44-8 ГБот 60 ГБ

Оценка ориентировочная — реальное потребление сильно зависит от объёма загружаемых файлов и картинок на страницах (BookStack хранит вложения на диске, а не в базе).

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

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

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

Готовый docker-compose.yml

Структура каталогов на сервере:

/opt/bookstack/
├── mysql-data/       # данные MariaDB
├── bookstack-data/    # загруженные файлы, изображения, аватары
├── .env
└── docker-compose.yml

Создаём каталоги и файл переменных окружения:

mkdir -p /opt/bookstack/{mysql-data,bookstack-data}
cd /opt/bookstack
nano .env

Содержимое .env — пароли сгенерируйте сами, не оставляйте примеры из статьи:

DB_ROOT_PASSWORD=замените_на_свой_root_пароль
DB_PASSWORD=замените_на_свой_пароль_приложения
APP_KEY=
APP_URL=https://knowledge.your-domain.example

APP_KEY заполнять руками не нужно — контейнер сгенерирует его сам при первом запуске и допишет в переменные окружения контейнера (не в файл .env), если оставить поле пустым в docker-compose.yml через APP_KEY: без значения — так и оставляем.

Сам docker-compose.yml:

services:
  mysql:
    image: mariadb:11
    container_name: bookstack-mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: bookstack
      MYSQL_USER: bookstack
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - ./mysql-data:/var/lib/mysql
    networks:
      - bookstack-net
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 6

  bookstack:
    image: lscr.io/linuxserver/bookstack:latest
    container_name: bookstack
    restart: unless-stopped
    depends_on:
      mysql:
        condition: service_healthy
    environment:
      PUID: "1000"
      PGID: "1000"
      TZ: "Europe/Moscow"
      APP_URL: ${APP_URL}
      DB_HOST: mysql
      DB_PORT: "3306"
      DB_DATABASE: bookstack
      DB_USERNAME: bookstack
      DB_PASSWORD: ${DB_PASSWORD}
    volumes:
      - ./bookstack-data:/config
    ports:
      - "127.0.0.1:6875:80"
    networks:
      - bookstack-net

networks:
  bookstack-net:
    driver: bridge

Образ lscr.io/linuxserver/bookstack выбран сознательно — сборка от LinuxServer.io упаковывает PHP-FPM, nginx и сам BookStack в одном контейнере, что упрощает файл по сравнению с «официальным» вариантом из документации проекта, где веб-сервер и приложение разнесены на два контейнера. Для небольшой базы знаний разница не критична, а обслуживать один контейнер приложения вместо двух проще.

Порт снова смотрит только на 127.0.0.1 — наружу отдаём через реверс-прокси с HTTPS, отдельный раздел ниже.

Первый запуск и вход администратором

Поднимаем стек:

docker compose up -d
docker compose logs -f bookstack

Первый старт занимает 30-90 секунд — контейнер ждёт готовности MySQL (за это отвечает healthcheck), затем накатывает миграции схемы. В логах должна появиться строка о старте PHP-FPM или nginx без ошибок подключения к базе.

Открываем http://<IP-сервера>:6875 (или домен, если прокси уже настроен). Данные для входа по умолчанию:

Email: admin@admin.com
Password: password

Первым делом — смените и почту, и пароль администратора: Профиль → Edit Profile. Оставленный дефолтный пароль на базе знаний, доступной снаружи, — обычная причина, по которой посторонний просто заходит и читает внутренние регламенты компании.

Дальше стоит сразу зайти в Settings → Registration и отключить публичную самостоятельную регистрацию, если пользователей планируется заводить вручную или через приглашения — по умолчанию форма регистрации в некоторых версиях включена.

Структура контента и права доступа

Создание иерархии в BookStack интуитивно: + Create New → Book, внутри книги — + New Page или + New Chapter для промежуточного уровня. Полки создаются отдельно и потом собирают книги через Edit Shelf → добавить книги.

Права доступа настраиваются на нескольких уровнях одновременно:

  • Роли (Settings → Roles) — набор глобальных разрешений: кто может создавать книги, кто редактировать чужие страницы, кто управлять пользователями. Есть готовые роли Admin, Editor, Viewer, можно создавать свои.
  • Permissions на конкретной сущности — у любой полки, книги, главы или страницы можно вручную переопределить, какие роли и какие конкретные пользователи имеют доступ (... → Permissions в меню сущности). Это удобно, когда, например, книга «Финансы» должна быть видна только определённому отделу, хотя остальная база знаний открыта всем.

Для входа через существующий каталог пользователей BookStack поддерживает LDAP и SAML2 из коробки — настройка через переменные окружения AUTH_METHOD=ldap и соответствующий блок LDAP_* параметров (сервер, base DN, атрибуты). Если в компании уже поднят LDAP-сервер, это избавляет от дублирования учёток между базой знаний и остальными системами.

Полезная функция для навигации по большой базе — тегирование страниц (Page → Edit → Tags) вместе с полнотекстовым поиском по индексу MySQL, без отдельного поискового движка — для баз в пределах нескольких тысяч страниц этого достаточно.

HTTPS и доступ извне через реверс-прокси

Отдавать вход с логином и паролем по HTTP нельзя — сессионные куки и пароли будут ходить открытым текстом. Проще всего закрыть это Caddy с автоматическим Let's Encrypt:

# /etc/caddy/Caddyfile
knowledge.your-domain.example {
    reverse_proxy 127.0.0.1:6875
}

Если Caddy на сервере ещё не настроен, есть отдельный разбор установки Caddy с авто-SSL на VPS. Вариант с Traefik тоже работает, если стек уже собран вокруг него — логика проксирования описана в статье про Traefik как реверс-прокси для Docker.

После того как домен заработал по HTTPS, обязательно поправьте APP_URL в .env на реальный https:// адрес и пересоздайте контейнер:

docker compose up -d --force-recreate bookstack

BookStack использует APP_URL для генерации всех внутренних ссылок и ассетов — если оставить старый адрес, часть ссылок и загруженных изображений будет вести на неверный домен или на http://, что в браузере даёт предупреждение о смешанном контенте.

Резервное копирование и обновление

Данные BookStack живут в двух местах: MySQL (структура книг, текст страниц, права) и каталог bookstack-data (загруженные файлы, обложки книг, аватары). Бэкапить нужно оба, отдельно друг от друга бэкап бессмысленен.

Дамп базы вручную:

docker exec bookstack-mysql sh -c 'exec mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" bookstack' > /opt/bookstack/backups/bookstack-$(date +%F).sql

Восстановление:

docker exec -i bookstack-mysql sh -c 'exec mysql -u root -p"$MYSQL_ROOT_PASSWORD" bookstack' < /opt/bookstack/backups/bookstack-2026-08-15.sql

Каталог с файлами копируется обычным tar:

tar czf /opt/bookstack/backups/bookstack-data-$(date +%F).tar.gz -C /opt/bookstack bookstack-data

Если на сервере уже настроен регулярный бэкап других сервисов, логичнее включить оба каталога BookStack в общий пайплайн — например, через BorgBackup в Docker Compose, а не поддерживать отдельный cron-скрипт под один сервис.

Обновление — стандартный pull + up:

docker compose pull bookstack
docker compose up -d bookstack

Миграции схемы БД накатываются автоматически при старте новой версии контейнера. Перед мажорным обновлением (смена первой цифры версии BookStack) стоит свежий дамп базы сделать заранее и заглянуть в changelog проекта — как и у большинства зрелых Laravel-приложений, обратные миграции на такой версии BookStack штатно не поддерживает.

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

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

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

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

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

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

BookStack можно связать с MySQL, вынесенной на отдельный сервер?

Да, укажите в DB_HOST адрес внешней базы вместо mysql. Если базу разворачиваете отдельно, пригодится статья про установку MySQL на VPS — там же про создание пользователя с правами только на нужную базу, что безопаснее, чем root-доступ из приложения.

Чем BookStack отличается от внутренней базы знаний с ИИ-поиском?

BookStack — классическая структурированная вики без встроенного семантического поиска: находит страницы по ключевым словам и тегам, но не отвечает на вопросы своими словами. Если нужен именно ИИ-ассистент поверх корпоративных документов, это отдельная задача — она разобрана в статье про внутреннюю базу знаний компании на VPS с ИИ.

Можно ли перенести контент из Confluence или другой вики в BookStack?

Прямого импорта из Confluence нет. Рабочий вариант — экспортировать страницы из Confluence в HTML или Markdown и завести в BookStack через API (POST /api/pages) скриптом, либо вставлять вручную через WYSIWYG-редактор постранично, если книг немного.

Что будет, если контейнер MySQL упадёт при активной записи страницы?

MariaDB с InnoDB (движок по умолчанию) устойчива к обрыву процесса — незавершённая транзакция откатится при следующем старте. Тем не менее регулярный бэкап остаётся обязательным: устойчивость к сбою процесса не защищает от испорченного диска или случайного DROP TABLE.

Сколько места реально займёт база знаний на 500 страниц?

Точную цифру дать нельзя — сильно зависит от количества и размера вложенных изображений, а не от текста самих страниц (текст в MySQL весит немного). Как ориентир: без активной работы с картинками и файлами база из нескольких сотен страниц обычно укладывается в единицы гигабайт, а основной рост диска идёт именно за счёт bookstack-data.

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

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

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