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

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

MAATRIX

Trello хорош для канбана, Asana — для списков задач, Notion — для таблиц и заметок, но команде часто нужно всё сразу, а платить за три подписки и синхронизировать данные между ними неохота. Focalboard закрывает это одним инструментом: канбан-доска, таблица, календарь и галерея — это просто разные представления одних и тех же карточек, переключаются одной кнопкой. Ниже — рабочий docker-compose.yml, с которым Focalboard поднимается на VPS за один заход, разбор перехода с SQLite на PostgreSQL и честный взгляд на то, в каком состоянии сейчас находится проект.

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

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

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

Чем Focalboard отличается от Trello, Asana и похожих досок

Focalboard изначально разрабатывался компанией Mattermost как открытая альтернатива Trello, Notion и Asana. Сейчас код живёт в репозитории mattermost-community/focalboard — отдельно от плагина Mattermost Boards (тот с 2024 года переехал в свой репозиторий и распространяется вместе с самим Mattermost). Personal Server Edition — тот вариант, который ставится через Docker и разбирается в этой статье, — развивается community-веткой отдельно от коммерческого продукта.

Что получаете из коробки:

  • Четыре представления одних данных — канбан-доска, таблица со столбцами-свойствами, календарь по датам и галерея карточек с превью. Переключение между ними не теряет и не дублирует карточки: это один и тот же датасет, показанный по-разному.
  • Кастомные свойства карточек — текст, число, дата, выпадающий список, чекбокс, ссылка на пользователя. В табличном виде свойства становятся столбцами, которые можно сортировать и фильтровать, — по сути мини-Notion.
  • Шаблоны досок — встроенные заготовки под спринты, роадмапы, личные задачи, плюс возможность сохранить свою доску как шаблон для повторного использования.
  • Одиночный бинарник — Focalboard написан на Go, серверная часть не требует Node.js в рантайме и по умолчанию хранит данные в SQLite-файле, без отдельной СУБД. Это заметно легче, чем связка Meteor + MongoDB у Wekan.
Trello (free)WekanFocalboard
Где хранятся данныесерверы Atlassianваш серверваш сервер
Представленияканбан, немного календаряканбан + свимлейныканбан, таблица, календарь, галерея
СУБДMongoDB (обязательно)SQLite (по умолчанию) или PostgreSQL
Кастомные свойства-столбцыPremiumограниченновстроены бесплатно
Активность разработкикоммерческий продуктстабильно развиваетсяищет community-мейнтейнеров

Из похожих self-hosted решений есть Wekan (чистый канбан, тяжелее по ресурсам из-за MongoDB) и Planka (тоже минималистичный канбан на MongoDB). Focalboard выигрывает у обоих гибкостью представлений — если нужна не только доска, а ещё и таблица с сортировкой по дедлайну, это единственный вариант из трёх без дополнительных сервисов.

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

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

/opt/focalboard/
├── data/           # SQLite-база и вложения
└── docker-compose.yml

Создаём каталог и файл:

mkdir -p /opt/focalboard/data
cd /opt/focalboard
nano docker-compose.yml

Минимальный рабочий вариант на SQLite — для команды до 10-15 человек этого достаточно, отдельная СУБД не нужна:

services:
  focalboard:
    image: mattermost/focalboard:latest
    container_name: focalboard
    restart: unless-stopped
    environment:
      TZ: "Europe/Moscow"
    volumes:
      - ./data:/data
      - ./config.json:/opt/focalboard/config.json
    ports:
      - "127.0.0.1:8000:8000"

Рядом кладём config.json — Focalboard читает настройки из файла, а не только из переменных окружения:

{
  "serverRoot": "https://board.your-domain.example",
  "port": 8000,
  "dbtype": "sqlite3",
  "dbconfig": "/data/focalboard.db",
  "useSSL": false,
  "webpath": "./pack",
  "filespath": "/data/files",
  "session_expire_time": 2592000,
  "session_refresh_time": 18000,
  "localOnly": false,
  "enableLocalMode": false
}

Два момента, которые чаще всего путают на старте:

  • serverRoot должен точно совпадать с внешним адресом, включая протокол. Это влияет на то, какие ссылки Focalboard генерирует для шаринга досок и на работу WebSocket-соединения для live-обновлений.
  • port в контейнере и порт, проброшенный наружу в docker-compose.yml, должны совпадать — если меняете один, меняйте и второй.

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

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

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

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

Первый запуск и создание рабочего пространства

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

docker compose up -d
docker compose logs -f focalboard

В логах должна появиться строка о старте HTTP-сервера на порту 8000, обычно это занимает несколько секунд — SQLite не нужно инициализировать отдельно, файл базы создаётся автоматически при первом запуске.

Открываем http://127.0.0.1:8000 через SSH-туннель (или сразу домен, если реверс-прокси уже настроен):

ssh -L 8000:127.0.0.1:8000 user@your-server-ip

При первом заходе Focalboard предлагает зарегистрировать аккаунт — в Personal Server Edition это происходит без email-подтверждения, регистрация полностью локальна на вашем сервере. Первый зарегистрированный пользователь автоматически становится администратором рабочего пространства.

Дальше стоит закрыть публичную регистрацию, если сервер смотрит в интернет: в упрощённой конфигурации выше отдельного флага для этого нет — доступ ограничивается на уровне реверс-прокси (Basic Auth или allowlist по IP). Для небольшой команды проще не публиковать адрес доски широко и добавлять участников ссылкой-приглашением.

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

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

# /etc/caddy/Caddyfile
board.your-domain.example {
    reverse_proxy 127.0.0.1:8000
}

Если Caddy на сервере ещё не настроен, шаги описаны в статье про установку Caddy с авто-SSL на Ubuntu 24.04. Caddy сам прокидывает заголовки Upgrade/Connection, нужные для WebSocket, поэтому live-обновления досок работают без дополнительной настройки.

Если вместо Caddy используете nginx, WebSocket-заголовки нужно прописать явно — иначе доска будет открываться, но обновления карточек у других участников появятся только после перезагрузки страницы:

location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Это одна из самых частых жалоб в issues проекта — доска «зависает» именно из-за непрокинутого WebSocket, а не из-за самого Focalboard. Если выбираете реверс-прокси с нуля, сравнение подходов есть в статье Traefik или Nginx Proxy Manager: что выбрать для сервера.

После того как домен заработал по HTTPS, обновите serverRoot в config.json на https://board.your-domain.example и пересоздайте контейнер:

docker compose up -d --force-recreate focalboard

PostgreSQL вместо SQLite: когда переходить и как

SQLite нормально держит команду в 10-15 человек с несколькими досками, но у него есть системное ограничение — один писатель одновременно. При активной параллельной работе (десятки людей двигают карточки в один момент) начинаются короткие блокировки на запись. Если это ваш случай, переходите на PostgreSQL.

Если PostgreSQL ещё не установлен, разберитесь в статье про установку и настройку PostgreSQL на VPS. Дальше создаём базу и пользователя:

sudo -u postgres psql -c "CREATE USER focalboard WITH PASSWORD 'сложный_пароль';"
sudo -u postgres psql -c "CREATE DATABASE focalboard OWNER focalboard;"

Меняем dbtype и dbconfig в config.json:

{
  "dbtype": "postgres",
  "dbconfig": "postgres://focalboard:сложный_пароль@host.docker.internal:5432/focalboard?sslmode=disable&connect_timeout=10"
}

Если PostgreSQL тоже поднят в Docker Compose рядом, вместо host.docker.internal указывайте имя сервиса в общей сети:

services:
  db:
    image: postgres:16
    container_name: focalboard-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: focalboard
      POSTGRES_PASSWORD: сложный_пароль
      POSTGRES_DB: focalboard
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    networks:
      - focalboard-net

  focalboard:
    image: mattermost/focalboard:latest
    container_name: focalboard
    restart: unless-stopped
    depends_on:
      - db
    volumes:
      - ./data:/data
      - ./config.json:/opt/focalboard/config.json
    ports:
      - "127.0.0.1:8000:8000"
    networks:
      - focalboard-net

networks:
  focalboard-net:
    driver: bridge

и dbconfig со значением postgres://focalboard:сложный_пароль@db:5432/focalboard?sslmode=disable. Focalboard сам создаёт нужные таблицы при первом подключении к пустой базе — штатной миграции данных из SQLite в PostgreSQL нет, поэтому переходить логичнее сразу на старте, а не когда в SQLite уже накопились десятки досок.

Резервное копирование, обновление и статус проекта

При SQLite бэкапить нужно один файл и каталог вложений:

docker exec focalboard sh -c "sqlite3 /data/focalboard.db '.backup /data/backup-$(date +%F).db'"
docker cp focalboard:/data/backup-$(date +%F).db /opt/focalboard/backups/
tar czf /opt/focalboard/backups/files-$(date +%F).tar.gz -C /opt/focalboard/data files

При PostgreSQL — обычный pg_dump по базе focalboard и тот же каталог вложений. Если на сервере уже настроен регулярный бэкап других сервисов, разумнее включить /opt/focalboard/data в общий пайплайн, например через restic в Docker Compose, а не городить для одной доски отдельный cron. Логика работы с volumes при переносе стека не отличается от любого другого Compose-проекта — она разобрана в статье про типы Docker volumes.

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

docker compose pull focalboard
docker compose up -d focalboard

Отдельно стоит сказать честно о статусе проекта: в 2023 году Mattermost прекратил приоритетную поддержку Focalboard силами штатной команды, а с 2024 года плагин для самого Mattermost (Boards) переехал в отдельный репозиторий и развивается вместе с ядром Mattermost. Standalone-версия, которая разбирается в этой статье, живёт как community-проект и ищет дополнительных мейнтейнеров — репозиторий активен, issues разбираются, но темп разработки не такой, как у коммерчески поддерживаемых продуктов. Для рабочей доски команды это не критично: приложение стабильно и не требует внешних сервисов, — но что-то критичное для бизнеса на нём строить стоит с оглядкой на этот риск. Пин конкретной версии образа (mattermost/focalboard:7.10.0 вместо latest) снижает риск неожиданностей при обновлении.

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

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

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

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

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

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

Можно ли перенести доски из Trello в Focalboard?

Да, есть штатный импорт из JSON-экспорта Trello (Меню доски → Печать и экспорт → Экспорт JSON, затем импорт через меню рабочего пространства Focalboard). Карточки, списки и метки переносятся, комментарии и вложения — не всегда полностью, стоит проверить результат на тестовой доске.

Focalboard поддерживает несколько рабочих пространств для разных команд?

Да, через мультитенантный режим, но конфигурация сложнее базового config.json из этой статьи и требует дополнительных флагов авторизации. Для одной команды описанного файла достаточно.

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

Почти всегда это несовпадающий serverRoot либо реверс-прокси без заголовков Upgrade/Connection для WebSocket (см. раздел про HTTPS) — Caddy пробрасывает их сам, nginx нужно настраивать явно.

Сколько ресурсов нужно для команды из 10 человек?

Ориентировочно хватает 1 vCPU и 1 ГБ RAM с SQLite — Focalboard написан на Go и не требует Node.js в рантайме, заметно легче Wekan с его Meteor + MongoDB. Это ориентир: реальное потребление зависит от числа досок и вложений.

Что делать, если проект окончательно перестанет развиваться?

Данные не заперты — SQLite-файл или PostgreSQL-дамп читаются штатными инструментами, а Focalboard можно заменить на форк сообщества или другой self-hosted инструмент через тот же механизм импорта/экспорта JSON.

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

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

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