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

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

MAATRIX

Wekan — открытый канбан-инструмент в духе Trello: колонки, карточки, чек-листы, метки, вложения. В отличие от облачных сервисов, self-hosted Wekan не привязывает вас к чужому аккаунту и не блокирует функции ради подписки. Но именно из-за связки Meteor + MongoDB у него есть свой набор граблей, которые не встречаются в типичном PHP- или Node-сервисе. Ниже — ошибки, с которыми чаще всего сталкиваются при развёртывании Wekan на своём сервере, и как их закрыть без переустановки с нуля.

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

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

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

Как устроен Wekan и почему это не «просто контейнер»

Wekan написан на Meteor.js — фреймворке, который держит соединение с клиентом через WebSocket и использует MongoDB не только как хранилище, но и как источник событий в реальном времени. Отсюда три особенности, которые ломают привычные схемы деплоя:

  • MongoDB нужна в режиме replica set, а не как обычный standalone-инстанс — иначе Meteor не может подписаться на изменения через oplog и переходит на менее эффективный опрос базы.
  • WebSocket-соединение должно проходить через reverse proxy без потерь — иначе доска «зависает» и не обновляется у других участников.
  • Приложение довольно прожорливо по памяти для своего функционала — Node.js-процесс Meteor держит в памяти скомпилированный клиентский бандл и активные подписки.

Минимальный docker-compose, от которого стоит отталкиваться:

services:
  wekan:
    image: wekanteam/wekan:latest
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - MONGO_URL=mongodb://wekandb:27017/wekan
      - ROOT_URL=https://board.example.com
      - MAIL_URL=smtp://user:pass@smtp.example.com:587/
      - WITH_API=true
      - WRITABLE_PATH=/data
      - PORT=8080
    volumes:
      - wekan-files:/data
    depends_on:
      - wekandb

  wekandb:
    image: mongo:6
    restart: unless-stopped
    command: mongod --oplogSize 128 --replSet rs0
    volumes:
      - wekan-mongodb:/data/db

volumes:
  wekan-files:
  wekan-mongodb:

После первого запуска реплика-сет нужно инициализировать вручную — это самая частая точка, где всё останавливается ещё до первого логина.

MongoDB: ошибка репликации и «зависшая» база

Классическая картина: контейнеры поднялись, docker compose ps показывает Up, но веб-интерфейс либо не открывается, либо крутит бесконечную загрузку. В логах wekandb при этом можно увидеть, что реплика-сет сконфигурирован (--replSet rs0 в команде запуска), но не инициализирован — Mongo ждёт команды rs.initiate() и до этого момента фактически не готова принимать часть операций так, как ожидает Meteor.

Решение — зайти в контейнер с базой и инициализировать сет вручную:

docker compose exec wekandb mongosh --eval "rs.initiate()"

Проверить статус:

docker compose exec wekandb mongosh --eval "rs.status()"

Если в выводе myState: 1 — узел стал PRIMARY, можно перезапускать Wekan:

docker compose restart wekan

Вторая частая ошибка на этом же этапе — несовпадение имени хоста в MONGO_URL с именем сервиса в compose-файле. Если сервис называется wekandb, а в MONGO_URL указано mongodb://mongo:27017/wekan, соединение просто не установится, и в логах Wekan будет постоянный MongoNetworkError. Имя после mongodb:// должно дословно совпадать с именем сервиса в docker-compose (Docker резолвит его через встроенный DNS).

Отдельно стоит учитывать MONGO_OPLOG_URL — если вы настраиваете отдельного пользователя с ограниченными правами для базы wekan, у него дополнительно должен быть доступ на чтение к базе local, где лежит oplog. Без этого доступа обновления карточек у других участников будут появляться с задержкой или не появляться совсем без перезагрузки страницы.

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

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

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

ROOT_URL, белый экран и бесконечный редирект

ROOT_URL — не просто справочное значение, а параметр, который Meteor использует при генерации ссылок, куки сессии и WebSocket-эндпоинта на клиенте. Три типичных симптома неправильного ROOT_URL:

  • Страница логина открывается, но после ввода пароля происходит редирект на http:// вместо https://, и браузер блокирует смешанный контент.
  • Интерфейс подгружается частично — видна шапка, но доски не отображаются, в консоли браузера ошибки WebSocket-подключения на неверный домен.
  • После входа за обратным прокси с завершением TLS на nginx/Traefik куки не сохраняются, и логин «слетает» при каждом обновлении страницы.

Правило простое: ROOT_URL должен точно совпадать с тем адресом, по которому пользователь открывает Wekan в браузере — с протоколом https://, без завершающего слэша, без порта, если порт стандартный (443).

ROOT_URL=https://board.example.com

Если Wekan стоит за nginx с завершением TLS, самому контейнеру этого достаточно — HTTPS обрабатывает прокси, а Wekan просто должен знать, что снаружи это выглядит именно так. Дополнительно полезно передать заголовки, чтобы Meteor понимал, что запрос уже пришёл по HTTPS:

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Если используете Let's Encrypt для сертификата и сталкивались с ошибками валидации при первом выпуске, это отдельная и довольно частая тема — разобрана в статье про типовые ошибки Let's Encrypt на сервере.

WebSocket за reverse proxy: доска не обновляется в реальном времени

Отдельно от ROOT_URL стоит проблема самого WebSocket-туннеля. Wekan показывает изменения других участников без перезагрузки страницы именно через постоянное соединение по wss://. Если reverse proxy не прокидывает заголовки Upgrade и Connection, соединение либо не устанавливается вовсе, либо рвётся через несколько десятков секунд простоя.

Для nginx обязательно нужны эти строки в том же location-блоке:

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;
}

proxy_read_timeout стоит явно увеличить — значение по умолчанию у nginx рассчитано на обычные HTTP-запросы, а не на постоянно открытое соединение, и слишком короткий таймаут будет обрывать WebSocket даже при корректно настроенном апгрейде заголовков.

Если вместо nginx используется Traefik как прокси перед Docker-контейнерами, WebSocket там обычно работает «из коробки» через тот же роутер, что и обычный HTTP-трафик — отдельной конфигурации Upgrade-заголовков не требуется, но стоит проверить, что таймауты у entrypoint не режут долгие соединения. Общий подход к Traefik перед контейнерами — в статье про Traefik как reverse proxy для Docker.

Проверить, что WebSocket вообще устанавливается, проще всего через вкладку Network в браузере — там должно появиться соединение с кодом ответа 101 Switching Protocols. Если вместо этого приходит 400 или 502, проблема почти всегда в конфигурации прокси, а не в самом Wekan.

Вложения, WRITABLE_PATH и место на диске

По умолчанию Wekan хранит загруженные файлы на диске контейнера по пути, заданному переменной WRITABLE_PATH. Если её не прокинуть отдельным volume, при пересоздании контейнера (например, после docker compose up -d --pull always) все вложения теряются — сама база в MongoDB останется целой, но ссылки на файлы будут вести в никуда.

environment:
  - WRITABLE_PATH=/data
volumes:
  - wekan-files:/data

Второй момент — размер загружаемых файлов. По умолчанию есть встроенное ограничение на размер вложения, и если команда регулярно прикрепляет к карточкам скриншоты или документы за пределами лимита, загрузка будет просто молча обрываться на клиенте. Ограничение регулируется переменной окружения MAX_IMAGE_PIXEL (для изображений) и общими лимитами на размер запроса на уровне reverse proxy — в nginx это client_max_body_size:

client_max_body_size 50m;

Если это значение стоит по умолчанию (обычно 1 МБ), а лимит в самом Wekan выше — прокси обрежет запрос раньше, чем он дойдёт до приложения, и пользователь увидит общую ошибку 413 без внятного объяснения.

Резервное копирование вложений и базы стоит настраивать отдельно и независимо друг от друга: дамп MongoDB через mongodump и архивирование volume с файлами. Общий подход к бэкапу docker-volume, применимый и здесь, разобран в статье про бэкап Docker volume на сервере.

Память, перезапуски контейнера и деградация со временем

Meteor-приложения известны тем, что со временем накапливают потребление памяти — особенно если Wekan используют одновременно несколько досок с большим количеством карточек и активных подписок на обновления. На серверах с небольшим объёмом RAM (1-2 ГБ) это часто приводит к тому, что через несколько дней работы контейнер начинает падать с OOM-килом, а docker compose logs wekan показывает внезапный обрыв процесса без явной ошибки внутри приложения.

Практические меры:

  • Задайте контейнеру restart: unless-stopped (как в примере выше) — это не решает саму проблему с памятью, но не даёт сервису надолго выпадать из строя после падения.
  • Настройте своп — на серверах с 1-2 ГБ RAM это часто разница между стабильной работой и постоянными перезапусками под нагрузкой; общий разбор темы — в статье про своп-файл: когда нужен и как настроить.
  • Ограничьте память контейнера явно через mem_limit в compose-файле — так падение будет предсказуемым и не утащит за собой MongoDB или другие сервисы на том же сервере:
services:
  wekan:
    mem_limit: 768m

Точных цифр по потреблению памяти в вашем случае никто не скажет заранее — оно сильно зависит от числа досок, участников и активных вкладок в браузере. Ориентир: для небольшой команды до 10-15 человек с несколькими досками обычно достаточно 1-2 ГБ RAM под связку Wekan + MongoDB, но при росте числа карточек и вложений стоит закладывать запас и следить за метриками, а не полагаться на цифру из статьи.

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

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

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

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

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

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

Обязательно ли использовать MongoDB как replica set, или можно standalone?

Формально Wekan запустится и на обычном standalone-инстансе, но реальное время обновления карточек у других пользователей будет работать через менее эффективный опрос базы вместо подписки на oplog. Для рабочего сервера лучше сразу настраивать replica set, как в примере выше.

Почему после смены домена или добавления HTTPS перестал открываться логин?

Почти всегда причина в ROOT_URL, который не обновили вместе с адресом. Значение должно точно совпадать с URL, по которому Wekan открывается в браузере, включая протокол https://.

Как создать первого администратора?

Первый зарегистрированный пользователь автоматически не становится админом — нужно один раз выполнить команду в контейнере с MongoDB, обновив поле isAdmin для нужного пользователя в коллекции users, либо воспользоваться встроенным скриптом set-admin, если он есть в вашей версии образа — детали отличаются между версиями, стоит свериться с документацией конкретного релиза, который вы разворачиваете.

Можно ли перенести Wekan на другой сервер без потери данных?

Да — переносится дамп MongoDB (mongodump/mongorestore) и содержимое volume с WRITABLE_PATH. Если оба переехали корректно и MONGO_URL на новом сервере указывает на восстановленную базу, вложения и карточки останутся на месте.

Почему письма-уведомления не приходят?

Проверьте переменную MAIL_URL — она задаёт SMTP-подключение целиком в виде строки smtp://user:pass@host:port/, и опечатка в любой части ломает отправку молча, без явной ошибки в интерфейсе. Логи контейнера wekan при этом обычно показывают ошибку подключения к SMTP при попытке отправки письма.

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

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

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