🛠 Подготовка: DNS и Фаервол

До запуска серверов необходимо подготовить сетевую инфраструктуру.

1.Настройка DNS-записей

В панели вашего регистратора доменов создайте следующие записи (замените 203.0.113.1 на внешний IP вашего сервера):

A-записи (Направляем домены на сервер):

A -> chat.example.com -> 203.0.113.1

A -> upload.chat.example.com -> 203.0.113.1

A -> conference.chat.example.com -> 203.0.113.1

A -> movim.example.com -> 203.0.113.1 (Для веб-клиента)

SRV-записи (Компасы маршрутизации для клиентов и федерации):

Для клиентов (Стандартное подключение):

    Имя (Хост): _xmpp-client._tcp.chat | Тип: SRV | Значение: 5 0 5222 chat.example.com.

Для серверов (Федерация S2S):

    Имя (Хост): _xmpp-server._tcp.chat | Тип: SRV | Значение: 5 0 5269 chat.example.com.

Для клиентов (Прямое шифрование - Direct TLS):

    Имя (Хост): _xmpps-client._tcp.chat | Тип: SRV | Значение: 5 0 5223 chat.example.com.

Для серверов (Прямое шифрование - Direct TLS):

    Имя (Хост): _xmpps-server._tcp.chat | Тип: SRV | Значение: 5 0 5270 chat.example.com.
  1. Настройка Фаервола (UFW / Панель хостинга)

Откройте необходимые порты для работы XMPP, передачи файлов и видео-трафика: Bash

Базовые порты XMPP и передача файлов SOCKS5

sudo ufw allow 5222/tcp sudo ufw allow 5269/tcp sudo ufw allow 5000/tcp

Порты для прямого шифрования (Direct TLS)

sudo ufw allow 5223/tcp sudo ufw allow 5270/tcp

Порты для видеозвонков (Coturn)

sudo ufw allow 3479/tcp sudo ufw allow 3479/udp sudo ufw allow 5349/tcp sudo ufw allow 50000:50500/udp

ШАГ 1: Подготовка плагинов Prosody

В версии 13.0 (Nightly) изменились пути к Lua-модулям, поэтому сервер может не находить важные расширения (например, для передачи файлов). Самое надежное решение — скачать свежие плагины прямо из официального репозитория разработчика и прокинуть их в контейнер.

Выполняем в терминале сервера:

1. Создаем структуру папок для нашего проекта

mkdir -p ./prosody/modules-custom
mkdir -p ./prosody/config/certs
mkdir -p ./prosody/data

2. Скачиваем ВСЕ свежие плагины во временную папку из репозитория

git clone --depth 1 --branch master https://github.com/bjc/prosody.git temp_prosody_git

3. Копируем папку plugins в нашу рабочую директорию модулей

cp -r temp_prosody_git/plugins/. ./prosody/modules-custom/

4. Удаляем временную папку (убираем мусор)

rm -rf temp_prosody_git

5. Выдаем права, чтобы Докер мог читать эти файлы

sudo chmod -R 755 ./prosody/modules-custom/

ШАГ 2: Выпуск “Тройного” сертификата

Групповые комнаты (MUC) и загрузка файлов (Upload) живут на виртуальных поддоменах. Без сертификата на эти поддомены другие серверы не смогут общаться с вашим.

Зайдите в Nginx Proxy Manager (вкладка SSL Certificates).

Выпустите один SSL-сертификат сразу на три домена, вписав их через запятую или Enter: chat.example.com, upload.chat.example.com, conference.chat.example.com

Скопируйте полученные ключи из папки NPM в папку Prosody:

fullchain.pem переименуйте в xmpp.crt

privkey.pem переименуйте в xmpp.key

Положите их в папку ./prosody/config/certs/ и выдайте права:

sudo chmod 644 ./prosody/config/certs/*

ШАГ 3: Клонирование и подготовка сборки Movim

Поскольку готового Docker-образа нет, мы собираем его из исходников. Это гарантирует наличие всех последних фиксов.

1. Клонируем репозиторий: В корневой папке проекта (рядом с папкой prosody) выполните:

git clone https://github.com/movim/movim.git

2. Создаем файл окружения .env: Внутри папки movim создайте файл .env. Он нужен для инициализации базы данных при сборке:

nano ./movim/.env

Вставьте туда ваши параметры (без пробелов около =):

POSTGRES_USER=movim
POSTGRES_PASSWORD=movim
POSTGRES_DB=movim

  1. Предварительная сборка: Чтобы Docker не выдавал ошибку «не найден», мы собираем и запускаем Movim отдельно в его папке:
cd movim

Создайте (отредактируйте) файл podman-compose.yml: Не забудьте отредактировать значения на ваши домены и ip адреса

services:
  db:
    image: 'docker.io/postgres:latest'
    environment:
      POSTGRES_USER: movim
      POSTGRES_PASSWORD: movim
      POSTGRES_DB: movim
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 2s
    networks:
      - movim_internal
  movim:
    build:
      context: ./
      dockerfile: Containerfile
    depends_on:
      db:
        condition: service_healthy
    environment:
      DB_HOST: db
      DB_PORT: 5432
      DB_NAME: movim
      DB_USER: movim
      DB_PASSWORD: movim
      DAEMON_URL: https://movim.tprod.space
      DAEMON_DEBUG: true
      DAEMON_VERBOSE: true
    ports:
      - 43564:8443
    networks:
      - movim_internal
      - npm_default
    extra_hosts:
      - "chat.example.com:203.0.113.1"
      - "upload.chat.example.com:203.0.113.1"
      - "conference.chat.example.com:203.0.113.1"
networks:
  movim_internal:
  npm_default:
    external: true
    name: npm_default

podman-compose.yml ожидает внешнюю сеть npm_default. Чтобы запуск не упал с ошибкой “network not found”, создай её вручную (если еще не создана):

docker network create npm_default

Запускаем сборку:

docker-compose -f podman-compose.yml up -d --build

Флаг --build обязателен, для создания образа из локальных исходников.

Ждем завершения сборки. Теперь образ существует локально.

  1. Останавливаем временный запуск и возвращаемся в папку проекта
docker-compose -f podman-compose.yml down
cd .. 

ШАГ 3.1: Создание podman-compose.yml

Теперь создаем основной файл оркестрации. Мы используем extra_hosts, чтобы Movim внутри контейнера точно знал, по какому IP искать чат-сервер, не полагаясь на внешние DNS-резолверы.

Создайте docker-compose.yml в корневой директории (например /opt/prosody) Не забудьте отредактировать значения ваших доменов и ip адресов.

services:
  # 1. Prosody (XMPP)
  prosody:
    image: prosodyim/prosody:13.0
    container_name: prosody
    restart: unless-stopped
    ports:
      - "5222:5222"
      - "5269:5269"
      - "5223:5223"
      - "5270:5270"
    volumes:
      - ./prosody/config/prosody.cfg.lua:/etc/prosody/prosody.cfg.lua:ro
      - ./prosody/config/certs:/etc/prosody/certs:ro
      - ./prosody/data:/var/lib/prosody
      - ./prosody/modules-custom:/usr/lib/prosody/modules-custom:ro
    networks:
      npm_default:
        aliases:
          - chat.example.com
          - upload.chat.example.com

  # 2. Coturn (Video)
  coturn:
    image: coturn/coturn:latest
    container_name: coturn-chat
    network_mode: "host"
    command:
      - -n
      - --external-ip=203.0.113.1
      - --static-auth-secret=SUPER_SECRET_123
      - --realm=chat.example.com
      - --listening-port=3479
      - --tls-listening-port=5349

  # 3. База данных для Movim
  db:
    image: 'docker.io/postgres:latest'
    container_name: movim_db
    environment:
      POSTGRES_USER: movim
      POSTGRES_PASSWORD: movim
      POSTGRES_DB: movim
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U movim -d movim"]
      interval: 2s
    networks:
      - movim_internal

  # 4. Movim (Веб-клиент) - ССЫЛАЕМСЯ НА ИСХОДНИКИ
  movim:
    build:
      context: ./movim
      dockerfile: Containerfile
    container_name: movim_app
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      DB_HOST: db
      DB_PORT: 5432
      DB_NAME: movim
      DB_USER: movim
      DB_PASSWORD: movim
      DAEMON_URL: https://movim.tprod.space
      DAEMON_DEBUG: "true"
      DAEMON_VERBOSE: "true"
    ports:
      - "43564:8443"
    networks:
      - movim_internal
      - npm_default
    extra_hosts:
      - "chat.example.com:203.0.113.1"
      - "upload.chat.exmple.com:203.0.113.1"

networks:
  movim_internal:
  npm_default:
    external: true
    name: npm_default

ШАГ 3.2: Запуск всей системы

Для запуска используем явное указание файла, так как мы назвали его в стиле Podman:

docker-compose -f podman-compose.yml up -d

ШАГ 4: Идеальный prosody.cfg.lua

Создайте файл ./prosody/config/prosody.cfg.lua. Этот монолитный конфиг включает современную безопасность, работу с веб-клиентами, CORS, загрузку тяжелых файлов, пуши и видеозвонки. Не забудьте отредактировать значения на ваши домены и ip адреса.

-- =======================================================================
-- PROSODY CONFIGURATION FOR DOCKER (v13.0+) & MOVIM
-- =======================================================================

-- 1. ГЛОБАЛЬНЫЕ НАСТРОЙКИ
-- -----------------------
plugin_paths = { "/usr/lib/prosody/modules-custom" }
local my_domain = "chat.example.com"
admins = { "admin@chat.example.com" } -- указать акаунт админа

contact_info = {
    abuse = { "mailto:admin@example.com", "xmpp:admin@chat.example.com" };
} -- почта и xmpp для жалоб от пользователей (например за спам)

external_addresses = { "203.0.113.1" } -- ВАШ ВНЕШНИЙ IP-АДРЕС
http_interfaces = { "*" } -- Мостик для Nginx (чтобы не было 502 ошибки)

-- Требуем шифрование для всех
c2s_require_encryption = true
s2s_require_encryption = true
authentication = "internal_hashed"

-- Порты для прямого шифрования (Direct TLS)
c2s_direct_tls_ports = { 5223 }
s2s_direct_tls_ports = { 5270 }

-- Разрешаем свободную регистрацию аккаунтов (XEP-0077)
allow_registration = true

-- Пути к данным внутри контейнера
data_path = "/var/lib/prosody"
pidfile = "/var/run/prosody/prosody.pid"


-- 2. МОДУЛИ (ПЛАГИНЫ)
-- -------------------
modules_enabled = {
    -- Ядро XMPP
    "roster"; "saslauth"; "tls"; "dialback"; "disco"; "ping";
    
    -- Мобильные клиенты, пуши и экономия батареи
    "cloud_notify"; "smacks"; "csi_simple"; "csi"; "carbons"; "mam";
    
    -- Сеть, прокси и обнаружение сервисов
    "proxy65"; "http_altconnect"; "server_info"; "external_services";
    
    -- Профили, аватарки, статусы, подписки и черные списки
    "pep"; "vcard4"; "vcard_legacy"; "avatar"; "profile"; "pubsub"; "pubsub_serverinfo"; "blocklist";
    
    -- ВАЖНО ДЛЯ MOVIM И БРАУЗЕРОВ (Веб-клиенты)
    "bosh"; "websocket"; "http_file_share";
    
    -- Регистрация аккаунтов прямо из приложения (XEP-0077)
    "register";

    -- ====================================================================
    -- ИСТОРИЯ АРХИТЕКТУРЫ (Устаревшие и продублированные модули)
    -- ====================================================================
    -- "server_contact_info"; -- УСТАРЕЛ: В Prosody 13+ встроен в 'server_info'
    -- "pep_vcard_avatar";    -- УСТАРЕЛ: Несовместим с новым движком 'pep'
};

-- Координаты сервера видеозвонков (Coturn) на альтернативных портах
external_services = {
    { type = "stun", host = "chat.example.com", port = 3479, transport = "udp" },
    { type = "stun", host = "chat.example.com", port = 3479, transport = "tcp" },
    { type = "turn", host = "chat.example.com", port = 3479, transport = "udp", secret = "SUPER_SECRET_123" },
    { type = "turn", host = "chat.example.com", port = 3479, transport = "tcp", secret = "SUPER_SECRET_123" },
    { type = "turns", host = "chat.example.com", port = 5349, transport = "tcp", secret = "SUPER_SECRET_123" }
}


-- 3. НАСТРОЙКИ ИСТОРИИ (MAM)
-- --------------------------
default_archive_policy = "roster" -- Сохранять переписку с контактами
archive_expires_after = "2y"      -- Хранить историю 2 года


-- 4. НАСТРОЙКИ HTTP И CORS (Критично для Movim)
-- ---------------------------------------------
http_cors_override = {
    bosh = {
        origin = "*";
        methods = { "GET", "POST", "OPTIONS" };
        headers = { "Content-Type", "Origin", "Authorization" };
        credentials = false;
    };
    websocket = {
        origin = "*";
        methods = { "GET", "POST", "OPTIONS" };
        headers = { "Content-Type", "Origin", "Authorization" };
        credentials = false;
    };
    http_upload = {
        origin = "*";
        methods = { "GET", "POST", "PUT", "OPTIONS", "HEAD" };
        headers = { "Content-Type", "Origin", "Authorization", "Content-Length" };
        credentials = false;
    };
}

-- 5. СЕРТИФИКАТЫ (SSL/TLS)
-- ------------------------
ssl = {
    key = "/etc/prosody/certs/xmpp.key";
    certificate = "/etc/prosody/certs/xmpp.crt";
    options = { "no_sslv2", "no_sslv3", "no_ticket", "no_compression", "cipher_server_preference", "single_dh_use", "single_ecdh_use" };
}

-- =======================================================================
-- ВИРТУАЛЬНЫЕ ХОСТЫ И КОМПОНЕНТЫ (СТРОГО В САМОМ НИЗУ ФАЙЛА!)
-- =======================================================================

-- 6. ВИРТУАЛЬНЫЙ ХОСТ (ОСНОВНОЙ ДОМЕН)
-- ------------------------------------
VirtualHost (my_domain)
    pubsub_serverinfo_service = "pubsub.chat.example.com"


-- 7. ГРУППОВЫЕ ЧАТЫ (MUC)
-- ------------------------------------
Component "conference.chat.example.com" "muc"
    name = "Общие комнаты"
    restrict_room_creation = false
    muc_log_by_default = true 
    muc_log_presences = false 
    modules_enabled = { "mam", "muc_mam" }


-- 8. КОМПОНЕНТ ЗАГРУЗКИ ФАЙЛОВ
-- ----------------------------
Component "upload.chat.example.com" "http_file_share"
    http_upload_file_size_limit = 50 * 1024 * 1024 -- Лимит 50 МБ
    http_upload_path = "/var/lib/prosody/http_upload" 


-- 9. СИСТЕМА ПУБЛИКАЦИЙ И ПОДПИСОК (PUBSUB)
-- ------------------------------------
Component "pubsub.chat.example.com" "pubsub"
    admins = { my_domain }

ШАГ 5: Настройка Nginx Proxy Manager (NPM)

Зайдите в панель управления NPM и создайте Proxy Hosts для маршрутизации веб-трафика внутрь Докер-контейнеров:

Домен: chat.example.com

    Forward Hostname / IP: prosody

    Forward Port: 5280

    Включить галочку Websockets Support.

    SSL: Выбрать созданный сертификат, включить Force SSL.

Домен: upload.chat.example.com

    Forward Hostname / IP: prosody

    Forward Port: 5280

    SSL: Выбрать созданный сертификат, включить Force SSL.

Домен: movim.example.com (Если нужен веб-клиент)

    Forward Hostname / IP: movim_app

    Forward Port: 43564 (Или 8443, в зависимости от того, как слушает контейнер внутри)

    Включить галочку Websockets Support.

    SSL: Выпустить отдельный сертификат для этого домена, включить Force SSL.

ШАГ 6: Первый запуск и тестирование! 🎉

Находясь в папке с docker-compose.yml, поднимите инфраструктуру:

docker compose up -d

Зарегистрируйте аккаунт администратора через внутреннюю консоль контейнера:

docker exec -it prosody prosodyctl register admin chat.example.com ВАШ_СЛОЖНЫЙ_ПАРОЛЬ

Рассылка

Получайте уведомления о новых постах и не только. Без спама.