Baïkal на сервере: частые ошибки и решения
Baïkal — лёгкий CalDAV/CardDAV сервер с веб-панелью, который многие ставят вместо тяжёлого Nextcloud, когда нужны только календари и контакты. На бумаге всё просто: PHP + SQLite (или MySQL), веб-установщик за пять минут — и клиенты на телефоне синхронизируются. На практике почти каждый, кто разворачивает Baïkal на своём VPS, упирается в один и тот же набор проблем: 401 при подключении клиента, «замёрзшую» синхронизацию, битые кодировки в кириллических событиях, permission denied на SQLite-файле после обновления. Ниже — разбор конкретных ошибок с решениями, которые реально помогают, без переписывания официальной документации.
Содержание
- Установка: на что обратить внимание с самого начала
- Ошибка 401 Unauthorized при подключении клиента
- Синхронизация «зависает» или клиент не видит новые события
- SQLite database is locked — что делать
- Проблемы с кодировкой в событиях и контактах
- Обновление Baïkal без потери данных
- Автоматизация: cron-задачи для обслуживания
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Установка: на что обратить внимание с самого начала
Baïkal ставится как обычное PHP-приложение, поэтому большинство будущих проблем закладывается на этапе установки.
Минимальный набор зависимостей на Ubuntu/Debian:
apt update
apt install -y php-fpm php-sqlite3 php-mbstring php-xml php-curl php-ctype php-simplexml nginx
Если планируете MySQL/MariaDB вместо SQLite (что разумно, если серверов и календарей будет много) — добавьте php-mysql.
Скачивание релиза, а не клона репозитория из ветки master:
cd /var/www
wget https://github.com/sabre-io/Baikal/releases/download/0.10.2/baikal-0.10.2.zip
unzip baikal-0.10.2.zip
mv baikal baikal
chown -R www-data:www-data baikal
Важный момент: каталоги Specific и config должны быть доступны на запись веб-серверу (www-data), а html/ — только на чтение. Если раздать chmod -R 777 «для простоты», получите классическую SQLite-проблему через пару недель — об этом ниже.
Пример конфига nginx с PHP-FPM (используйте актуальную версию PHP из вашего дистрибутива):
server {
listen 443 ssl;
server_name caldav.example.com;
root /var/www/baikal/html;
index index.php;
ssl_certificate /etc/letsencrypt/live/caldav.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/caldav.example.com/privkey.pem;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}
Про выпуск сертификата и типичные грабли Let's Encrypt — отдельно в статье про Let's Encrypt SSL на сервере, а если решите вынести Baïkal за общий reverse proxy — пригодится материал про nginx как reverse proxy.
Ошибка 401 Unauthorized при подключении клиента
Самая частая жалоба: веб-панель Baïkal открывается и работает, а вот CalDAV/CardDAV клиент (Thunderbird, DAVx5, iOS Календарь) получает 401 при попытке подключиться по адресу https://caldav.example.com/dav.php.
Причины и что проверить по порядку:
- Неверный URL. Клиенты часто путают корневой адрес Baïkal с адресом DAV-эндпоинта. Правильный путь —
/dav.php/(или/dav.php/principals/USERNAME/для некоторых клиентов), а не/admin/и не корень домена. - Basic Auth срезается на уровне nginx/reverse proxy. Если Baïkal стоит за дополнительным прокси (Cloudflare, другой nginx перед fastcgi), заголовок
Authorizationиногда не пробрасывается. Проверьте, что в конфиге нетproxy_set_header Authorization "";, случайно попавшего туда из шаблона другого сервиса. - PHP не видит заголовок Authorization через PHP-FPM. Иногда FastCGI не передаёт
Authorizationв$_SERVER. Лечится добавлением в конфиг nginx:
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
- Пароль пользователя не совпадает с тем, что реально сохранён. Baïkal хранит пароли с солью в своей SQLite/MySQL базе — если вы восстанавливали базу из бэкапа отдельно от файлов конфигурации, пароли могли «отвязаться» от текущей соли шифрования в
config/baikal.yaml.
Проверить последнюю причину быстро: создайте нового тестового пользователя через веб-панель и попробуйте подключиться под ним. Если новый пользователь подключается, а старый — нет, проблема именно в рассинхроне пароля/соли после восстановления бэкапа.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверСинхронизация «зависает» или клиент не видит новые события
Второй по частоте класс проблем — клиент подключился, авторизовался, но события создаются с задержкой, дублируются или вовсе не долетают в обе стороны.
Основные причины:
- ETag/CTag не пересчитывается. Baïkal (как и любой sabre/dav сервер) отслеживает изменения через CTag календаря. Если вы правили события напрямую в базе SQL, минуя API, CTag не обновится и клиент решит, что изменений нет. Не редактируйте таблицы
calendarobjectsвручную — только через клиент или API. - Часовой пояс сервера отличается от ожидаемого. Проверьте
date.timezoneвphp.ini(обычно/etc/php/8.3/fpm/php.ini), поставьте нужный (date.timezone = Europe/Moscow) и обязательно перезапуститеsystemctl restart php8.3-fpm— иначе изменение не подхватится. - Клиент кэширует старую версию календаря. Особенно этим грешит Thunderbird/Lightning — иногда помогает только полное удаление и повторное добавление календаря, а не «обновить».
- Слишком частый polling создаёт гонки. Если несколько устройств синхронизируются одновременно с интервалом в минуту, при высокой нагрузке на SQLite (см. ниже) часть запросов может завершаться с ошибкой блокировки, и клиент трактует это как «нет изменений».
Для диагностики полезно временно включить лог nginx с телом запросов CalDAV (PROPFIND, REPORT) — код 207 Multi-Status это нормально, а 500 или 503 указывают на проблему на стороне PHP/SQLite.
SQLite database is locked — что делать
Классическая ошибка при использовании SQLite-бэкенда (по умолчанию в Baïkal) под нагрузкой от нескольких синхронизирующихся клиентов:
PDOException: SQLSTATE[HY000]: General error: 5 database is locked
SQLite использует блокировку на уровне файла и плохо переносит параллельную запись — а именно это и происходит, когда 3-4 устройства пытаются синхронизироваться одновременно (что для семьи или небольшой команды — обычное дело).
Что реально помогает:
- Проверить права на директорию с базой, а не только на сам файл — SQLite создаёт временный
-journal/-walфайл рядом с основным, и если веб-сервер не может писать в директорию, блокировки возникают чаще:
chown -R www-data:www-data /var/www/baikal/Specific
chmod -R 750 /var/www/baikal/Specific
- Проверить, что диск не в режиме сетевой файловой системы (NFS, SMB-mount, сетевые тома у облачных провайдеров) — SQLite с блокировками на сетевых ФС работает ненадёжно в принципе.
- Мигрировать на MySQL/MariaDB, если пользователей и устройств больше 3-5. Это не «костыль», а штатный сценарий — Baïkal поддерживает MySQL из коробки, переключение делается на этапе установки (или заново прогнав
Specific/config/database.phpруками, если инсталлятор уже отработал). MySQL нормально держит параллельные записи от нескольких CalDAV-клиентов, в отличие от SQLite. Про типичные проблемы уже с MySQL — в статье MySQL на сервере: частые ошибки и решения.
Кратко: SQLite подходит для 1-3 устройств и тестового стенда — установка проще некуда, но параллельная запись это её слабое место. MySQL/MariaDB требует отдельного сервера БД, зато штатно держит десятки клиентов — разумный выбор для семьи или команды.
Проблемы с кодировкой в событиях и контактах
Отдельная головная боль для русскоязычных пользователей — кракозябры в названиях событий, кириллических именах контактов или при импорте .ics файлов из других систем (например, из Google Calendar или Outlook).
Типичные причины:
- Кодировка соединения с MySQL не выставлена в utf8mb4. Если вы используете MySQL-бэкенд, проверьте в
Specific/config/database.php(или через веб-установщик), что подключение идёт с корректной кодировкой. Эмодзи и часть символов в описаниях событий требуют именноutf8mb4, а неutf8— старая кодировкаutf8в MySQL это фактически utf8mb3, который не вмещает 4-байтовые символы. - Локаль PHP не установлена. Проверьте вывод
php -i | grep -i locale— если стоитCвместоru_RU.UTF-8илиen_US.UTF-8, часть строковых операций может ломать многобайтовые символы. - Импортируемый .ics файл в другой кодировке. Если файл выгружен из старой версии Outlook, он иногда сохраняется не в UTF-8. Перед импортом прогоните через
iconv:
iconv -f windows-1251 -t utf-8 old_calendar.ics -o calendar_utf8.ics
Затем импортируйте конвертированный файл через веб-панель (раздел Import calendar).
Обновление Baïkal без потери данных
Обновление — момент, когда чаще всего всё ломается, потому что структура каталогов в новых релизах менялась несколько раз (в частности, вынос Specific из корня в отдельный каталог в одной из версий 0.x).
Безопасная последовательность:
- Сделайте полный бэкап перед любым обновлением — и файлы, и базу:
tar -czf baikal-backup-$(date +%F).tar.gz /var/www/baikal/Specific /var/www/baikal/config
# для SQLite база обычно лежит в Specific/db/db.sqlite
Если используете MySQL — добавьте mysqldump этой базы отдельно. Общие практики бэкапа MySQL разобраны в статье бэкап MySQL на сервере.
- Скачайте новый релиз в отдельную папку, не поверх текущей, и перенесите
Specificиconfigиз старой установки в новую — именно эти каталоги хранят пользовательские данные и настройки, а не код:
cd /var/www
wget https://github.com/sabre-io/Baikal/releases/download/X.Y.Z/baikal-X.Y.Z.zip
unzip baikal-X.Y.Z.zip -d baikal-new
cp -r baikal/Specific baikal/config baikal-new/
chown -R www-data:www-data baikal-new
- Переключите symlink или root в nginx на новую директорию только после проверки прав.
- Первый запуск после обновления может потребовать миграцию схемы БД — Baïkal делает это автоматически при первом обращении к
/admin/. Зайдите туда сразу после переключения и убедитесь, что панель открывается без ошибок, прежде чем переключать клиентов на боевой адрес.
Если обновление сломало установку — откатитесь на бэкап из шага 1, а не пытайтесь чинить «на живую»: сломанная схема БД в CalDAV-сервере может привести к тихой потере событий, которую вы заметите не сразу.
Автоматизация: cron-задачи для обслуживания
Baïkal не требует сложного обслуживания, но пара регулярных задач всё же полезна: ротация логов PHP-FPM/nginx и автоматический бэкап базы.
Пример cron-задачи для ежедневного бэкапа SQLite-базы (файл копируется, а не блокируется на чтение, что безопасно для SQLite при отсутствии активной транзакции в момент копирования):
# /etc/cron.d/baikal-backup
0 3 * * * www-data cp /var/www/baikal/Specific/db/db.sqlite /var/backups/baikal/db-$(date +\%Y\%m\%d).sqlite
0 4 * * 0 www-data find /var/backups/baikal/ -name "*.sqlite" -mtime +30 -delete
Для MySQL-бэкенда аналогичная задача делается через mysqldump с последующей ротацией. Подробнее про синтаксис cron и типичные ошибки с путями — в статье cron-задачи на сервере: частые ошибки и решения.
Отдельно стоит настроить мониторинг доступности эндпоинта /dav.php/ curl-запросом с проверкой кода ответа (401 без авторизации — нормальный «живой» ответ, а таймаут или 502 — сигнал проблемы).
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Baïkal требует много ресурсов сервера?
Нет, это одно из самых лёгких CalDAV/CardDAV решений — 512 МБ RAM и 1 vCPU достаточно даже для нескольких десятков пользователей на SQLite. Для команды от 10+ человек лучше сразу брать MySQL-бэкенд и сервер с запасом по RAM.
Можно ли перенести Baïkal на другой сервер без потери календарей?
Да — переносится содержимое Specific и config (плюс дамп MySQL, если используется). Важно сохранить неизменным файл с солью шифрования, иначе пароли пользователей перестанут работать и их придётся сбрасывать заново.
Почему после смены домена перестали работать клиенты?
Baïkal не хранит URL внутри базы, но клиенты (особенно iOS и DAVx5) кэшируют адрес принципала. После смены домена нужно удалить и заново добавить учётную запись календаря на каждом устройстве, простого «изменить адрес сервера» в настройках часто недостаточно.
Стоит ли использовать Baïkal вместо Nextcloud только ради календаря?
Если вам нужны исключительно календари и контакты без файлового хранилища, заметок и остального экосистемы — да, Baïkal заметно легче по требованиям к серверу и проще в обслуживании, чем разворачивать Nextcloud целиком ради CalDAV.
Поддерживает ли Baïkal шаринг календарей между пользователями?
Да, через веб-панель администратора можно выдавать доступ на чтение или запись другим пользователям к конкретному календарю — это делается в разделе Users & resources.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →