# Mattermost на Kraken — деплой (arm64) > Создано 2026-09-26. Статус: **в работе** — образ собирается, контейнеры поднимаются. > Часть проекта [[personal/projects/balda/balda-mm-delegation-plan]]. --- ## 🔴 Главная находка: официального arm64-образа Mattermost НЕТ `mattermost/mattermost-team-edition` — **только `linux/amd64`**. Проверено `docker manifest inspect` по тегам `latest`, `release-12`, `release-10`, `11.11.1`, `10.11.24`, `9.11`, `release-8` — везде `architecture: amd64`. То же у `mattermost-enterprise-edition` и `mattermost-preview`. Официальная позиция Mattermost (KB `53131125053204`, на 2026-09-01): **ARM64 «Not officially supported for production, no roadmap commitment».** Публикуются некоторые arm64-сборки, но production не поддерживается; официально поддержан только amd64. ⚠️ **`linuxserver/mattermost` НЕ существует** — `pull access denied`. На Docker Hub его нет. Не искать его снова (я потратил на это время). ### Решение: собираем образ сами из ОФИЦИАЛЬНОГО бинаря Mattermost **публикует arm64-бинарники** на release-сервере — просто не пакует их в образы. Поэтому образ собирается локально на Kraken из официального tarball. Это production-путь: свой Dockerfile + upstream-бинарь, **без сторонних community-образов**. Проверка доступности (HTTP 200, `content-length: 157689127`): ``` https://releases.mattermost.com/11.11.1/mattermost-team-11.11.1-linux-arm64.tar.gz ``` Работает и `mattermost--linux-arm64.tar.gz` (без `team`). Префикс `mattermost-team-` — Team Edition, это то, что нужно. **Альтернативы, которые НЕ выбраны:** | Вариант | Почему нет | |---|---| | `cyberman443/mattermost-arm64` | сторонний, 5 мес. давности, непроверенная сборка | | `ghcr.io/antonsatt/mattermost-arm64` | сторонний; CI собирает weekly, но доверия меньше, чем своему Dockerfile | | `mmtrt/…`, `ctlove0523/…`, `bitnami/mattermost` | не существуют / нет arm64 | Референс структуры Dockerfile: `github.com/AntonSatt/mattermost-arm64` (там `ubuntu:noble` + официальный tarball; **нюанс — в их ARG по умолчанию enterprise-путь без `-team`**, у нас явно team). --- ## Файлы и пути | Что | Путь | |---|---| | Каталог compose | `/home/kraken/mattermost/` | | `docker-compose.yml` | `/home/kraken/mattermost/docker-compose.yml` (0600) | | `.env` (пароль postgres) | `/home/kraken/mattermost/.env` (0600, 3 строки) | | Данные и тома | `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/mattermost/{postgres,data,logs,plugins,config,client-plugins,bleve-indexes}` | | Dockerfile сборки | `/home/kraken/mattermost-build/Dockerfile` | | Образ | `mattermost-team-arm64:11.11.1` (+ `:latest`) | | Скрипты деплоя (Mac) | `/tmp/mm-deploy/deploy-mattermost.sh`, `/tmp/mm-deploy/build-mattermost-arm64.sh` | ⚠️ Путь `49e8f586…` — **симлинк** на реальный `6194539b…`. Все сервисы используют старый путь. ### `.env` формат (3 строки, значения не в доке) ``` PGPW=<24 байта, openssl rand -hex 24> PGUSR=mmuser PGDBNAME=mattermost ``` Генерируется **на хосте** при первом запуске скрипта; повторный запуск не перезаписывает (`if [ -s .env ]; then exists_kept`). ### `docker-compose.yml` — ключевые параметры | Ключ | Значение | Почему | |---|---|---| | образ MM | `mattermost-team-arm64:11.11.1` | вместо официального amd64 | | порт | `8065:8065` | был свободен (проверено `ss -tln`) | | postgres | `postgres:16-alpine` | arm64 есть | | `MM_SQLSETTINGS_DRIVERNAME` | `postgres` | | | `MM_SQLSETTINGS_DATASOURCE` | `postgres://mmuser:@postgres:5432/mattermost?sslmode=disable&connect_timeout=10` | пароль подставляется **в момент генерации compose** из `.env` | | `MM_SERVICESETTINGS_SITEURL` | `http://192.168.1.14:8065` (LAN, временно) | → поменять на `https://mm.qentra.top` после настройки туннеля | | `MM_SERVICESETTINGS_ENABLELOCALMODE` | `true` | нужен для healthcheck `mmctl --local` | | `MM_FILESETTINGS_DIRECTORY` | `/mattermost/data` | | | healthcheck MM | `curl -fsS http://localhost:8065/api/v4/system/ping` | curl есть в ubuntu:noble-базе | | healthcheck PG | `pg_isready -U mmuser -d mattermost` | | | `restart` | `unless-stopped` | | | `depends_on` | postgres `service_healthy` | MM не стартует раньше БД | | сеть | bridge `mm-net` (не `host`) | MM не требует host-сети, в отличие от cloudflared | **Питфолл:** `MM_SQLSETTINGS_DATASOURCE` — это URL, Mattermost читает env **литерально**, интерполяция `${PGPW}` внутри значения не работает. Поэтому DSN собирается на хосте через `printf` при записи compose, а не через `env_file`. --- ## Команды ```bash # Сборка образа (на Kraken, ~минуты; запускать в background) bash /tmp/mm-deploy/build-mattermost-arm64.sh # Деплой (идемпотентный) bash /tmp/mm-deploy/deploy-mattermost.sh # Проверка ssh kraken "curl -s http://localhost:8065/api/v4/system/ping" ssh kraken "docker ps --format '{{.Names}}\t{{.Status}}' | grep -E 'mattermost|mm-postgres'" ``` ### Питфоллы, найденные на месте 1. **`/srv/.../docker/` — `root:root 755`** → `kraken` НЕ может создать подкаталог. Нужен `sudo -n mkdir -p` + `sudo -n chown -R 1000:1000`. `sudo -n true` на Kraken работает **без пароля** (проверено, exit 0). 2. **`sudo` через SSH требует `< /dev/null`** — иначе `Connection closed by remote host`. (Подтверждено, питфолл из `kraken-access` актуален.) 3. **Нет `ifconfig`** на Kraken — использовать `ip addr` / `ss`. 4. **Один ssh-heredoc вместо десятка round-trip'ов** — `ssh kraken 'bash -s' <<'REMOTE'`. Быстрее и надёжнее, чем отдельные вызовы. 5. **`docker manifest inspect` на приватном/несуществующем образе** даёт `unauthorized: authentication required`, а не `not found` — не путать с «образ существует». --- ## Cloudflare Tunnel — что настроить (делает Alex) Туннель `kraken` уже существует, remote-managed по `TUNNEL_TOKEN`, `network_mode: host`. **Zero Trust → Networks → Tunnels → `kraken` → Public Hostnames → Add:** | Поле | Значение | |---|---| | Subdomain | `mm` | | Domain | `qentra.top` | | Path | *(пусто)* | | Service Type | `HTTP` | | URL | `localhost:8065` | ⚠️ **`httpHostHeader` НЕ ставить** — в отличие от `kraken.qentra.top`, где он обязателен. ⚠️ Без Access-Applications хостнейм `mm.qentra.top` **публично открыт в интернет**. В Team Edition регистрация по умолчанию `open` — закрывать политикой Access или выключать самостоятельную регистрацию в System Console. **После добавления хостнейма** — поменять `MM_SERVICESETTINGS_SITEURL` на `https://mm.qentra.top` и перезапустить MM. Иначе MM отдаёт ссылки на `192.168.1.14`, и WebSocket-клиент может не подключиться. **Приёмка = вход в `https://mm.qentra.top` в браузере**, не «curl отвечает». --- ## Состояние (2026-09-26) | Шаг | Статус | |---|---| | Каталоги на HDD | ✅ созданы, `kraken:kraken` | | `.env` с паролем | ✅ создан, 0600, значения не логировались | | `docker-compose.yml` | ✅ записан, проверено: DSN без плейсхолдера | | Dockerfile + сборка arm64-образа | 🔄 запущена в фоне | | `docker compose up -d` | ⏳ ожидает образ | | Проверка `/api/v4/system/ping` | ⏳ | | Cloudflare hostname `mm.qentra.top` | ⏳ делает Alex | | `SITEURL` → внешний | ⏳ после туннеля | ## Связанные заметки - [[personal/projects/balda/balda-mm-delegation-plan]] — общий план MM + делегирования - [[family/how-to/kraken-access]] — доступ к Kraken, cloudflared, порты - [[family/how-to/openmediavault-rpi5]] — структура Docker на Kraken - [[personal/tech/docker-data-root-mount-race]] — почему том на HDD, а не на SD - [[personal/projects/balda/balda-mattermost-connector]] — коннектор MM для Балды