Files
remnawave-minishop/docs/deployment.md
T
2026-05-13 22:34:24 +03:00

15 KiB
Raw Blame History

Развертывание

Документ описывает запуск через Docker Compose, маршруты вебхуков и варианты reverse proxy. Перед запуском заполните .env по configuration.md.

Docker Compose

Локальная сборка:

docker compose up -d --build
docker compose logs -f remnawave-minishop

Запуск из готового образа:

IMAGE_TAG=3.1.0 docker compose -f docker-compose-remote-server.yml up -d

docker-compose-remote-server.yml можно использовать как шаблон и заменить image: на нужный образ. По умолчанию используется ghcr.io/3252a8/remnawave-minishop:latest.

Права на ./data

Если в Compose включен bind mount ./data:/app/data или ./data:/app/data:rw, каталог на хосте должен быть доступен на запись пользователю контейнера. Контейнер запускает приложение от appuser с UID 10001; запуск Docker от root на Ubuntu не делает этот каталог writable внутри контейнера, если на хосте он принадлежит root:root с обычными правами 755.

Перед запуском или после добавления mount выполните на сервере из каталога проекта:

mkdir -p data/webapp-logo data/webapp-emoji
chown -R 10001:10001 data
chmod -R u+rwX data
docker compose up -d --force-recreate remnawave-minishop

Проверка прав:

docker compose exec remnawave-minishop sh -lc 'id; ls -ldn /app/data /app/data/webapp-emoji; touch /app/data/webapp-emoji/test && rm /app/data/webapp-emoji/test'

Если проверочный touch проходит без Permission denied, Web App сможет сохранять каталог тарифов, кеш WEBAPP_LOGO_URL в /app/data/webapp-logo и кеш animated emoji в /app/data/webapp-emoji.

Обновление версии

Образ приложения: ghcr.io/3252a8/remnawave-minishop. Тег задаётся переменной окружения IMAGE_TAG (в Compose подставляется как ${IMAGE_TAG:-latest}). Для продакшена разумно закрепить конкретный тег релиза вместо latest, чтобы обновляться осознанно и иметь откат.

Запуск из готового образа (docker-compose-remote-server.yml или свой файл с тем же шаблоном):

  1. Сделайте резервную копию базы (особенно перед крупными обновлениями) — см. раздел «Резервная копия и восстановление PostgreSQL» ниже на этой странице.
  2. Укажите нужный тег, например в .env: IMAGE_TAG=3.2.0 (или экспортируйте переменную перед командой).
  3. Подтяните образ и пересоздайте контейнер приложения (БД при этом не удаляется, том данных сохраняется):
docker compose -f docker-compose-remote-server.yml pull remnawave-minishop
docker compose -f docker-compose-remote-server.yml up -d --no-deps remnawave-minishop

При необходимости перезапустите оба сервиса: docker compose -f docker-compose-remote-server.yml up -d. Флаг --force-recreate добавляют, если нужно гарантированно пересоздать контейнер при неизменённом образе.

Локальная сборка из репозитория (ваш Dockerfile):

git pull
docker compose up -d --build

После обновления проверьте логи (docker compose logs -f remnawave-minishop), работу бота, вебхуков и Web App. Совместимость с панелью Remnawave — см. раздел «Совместимость» в README.md.

Резервная копия и восстановление PostgreSQL

Имя контейнера БД в типичном Compose — remnawave-minishop-db. Учётные данные уже передаются в контейнер через env_file: .env, поэтому надёжнее вызывать pg_dump / psql внутри контейнера через sh -c '...', чтобы переменные раскрылись там, а не на хосте.

Если написать на хосте pg_dump -U "$POSTGRES_USER" ... без экспорта переменных из .env, подставится пустая строка: PostgreSQL тогда берёт имя пользователя ОС (часто root) и выдаёт FATAL: role "root" does not exist. В PowerShell $POSTGRES_DB из файла .env сам не подхватывается — пустое имя базы даёт у dropdb ошибку missing required argument database name.

Вариант A (рекомендуется): переменные только внутри контейнера:

docker exec remnawave-minishop-db sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB"' > backup.sql

Вариант B: сначала загрузить .env в текущую сессию на сервере, затем обычная команда (переменные раскроются на хосте):

set -a && source .env && set +a
docker exec remnawave-minishop-db pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" > backup.sql

Такой файл удобно хранить вне сервера. При необходимости добавьте к pg_dump параметры сжатия или расписание через cron.

Восстановление из backup.sql

Дамп выше — обычный текст SQL без удаления существующих объектов. Чтобы накатить его на чистую базу с тем же именем:

  1. Остановите приложение, чтобы не было записей в БД:
docker compose -f docker-compose-remote-server.yml stop remnawave-minishop

(или ваш файл Compose без -f, если работаете из каталога проекта.)

  1. Удалите базу и создайте пустую с тем же именем — пользователь из .env в официальном образе PostgreSQL обычно суперпользователь и может это сделать:
docker exec remnawave-minishop-db sh -c 'dropdb -U "$POSTGRES_USER" --if-exists "$POSTGRES_DB"'
docker exec remnawave-minishop-db sh -c 'createdb -U "$POSTGRES_USER" "$POSTGRES_DB"'

Имя базы у dropdb — последний аргумент; флаг --if-exists ставьте перед ним (иначе клиент может неверно разобрать командную строку).

Если dropdb сообщает, что база занята, убедитесь, что остановлен сервис remnawave-minishop и к базе нет других подключений.

  1. Восстановите данные из файла на хосте. Переменные снова должны раскрываться внутри контейнера; на стороне psql имеет смысл включить ON_ERROR_STOP, чтобы при первой ошибке в дампе команда завершилась с ненулевым кодом.

Bash:

docker exec -i remnawave-minishop-db sh -c 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"' < backup.sql

PowerShell (перенаправление < в docker exec часто не подходит; надёжнее передать дамп в stdin через pipe; явная UTF-8, чтобы кириллица в комментариях/SQL не исказилась):

Get-Content backup.sql -Encoding utf8 | docker exec -i remnawave-minishop-db sh -c 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'

Если путь к файлу содержит пробелы, используйте кавычки: Get-Content "E:\backups\backup.sql" -Encoding utf8 | ...

  1. Запустите приложение снова:
docker compose -f docker-compose-remote-server.yml start remnawave-minishop

Проверьте логи и работу бота. При ошибках импорта убедитесь, что версия приложения совместима со схемой в дампе (после обновлений бота иногда нужны шаги из changelog релиза).

Замечание про «накат поверх» без пересоздания БД

Если нужно применить дамп к уже заполненной базе без dropdb, создавайте резервные копии с pg_dump --clean --if-exists — в файл попадут команды DROP перед CREATE, и psql сможет перезаписать объекты. Это разрушительно для текущих данных; перед таким сценарием сделайте отдельный свежий бэкап.

Порты

Порт Назначение
WEB_SERVER_PORT (8080) Telegram webhook, платежные вебхуки, Remnawave webhook.
WEBAPP_SERVER_PORT (8081) Web App / Mini App.

Web App не должен проксироваться на WEB_SERVER_PORT.

Маршруты вебхуков

Проксируйте платежные и системные вебхуки на WEB_SERVER_PORT:

  • https://<webhook-domain>/webhook/yookassa -> http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/yookassa;
  • https://<webhook-domain>/webhook/freekassa -> http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/freekassa;
  • https://<webhook-domain>/webhook/platega -> http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/platega;
  • https://<webhook-domain>/webhook/severpay -> http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/severpay;
  • https://<webhook-domain>/webhook/cryptopay -> http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/cryptopay;
  • https://<webhook-domain>/webhook/panel -> http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/panel.

Telegram webhook устанавливается приложением, если задан WEBHOOK_BASE_URL. Полный URL — это базовый URL без токена в пути, с суффиксом /tg/webhook (например https://webhook.domain.com/tg/webhook). Прокси должен передавать на приложение POST-запросы по этому пути на WEB_SERVER_PORT.

Nginx рядом с Remnawave

Пример upstream и server-блока для домена вебхуков:

upstream remnawave-minishop {
    server remnawave-minishop:8080;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    "" close;
}

server {
    server_name webhook.domain.com;
    listen 443 ssl;
    http2 on;

    ssl_certificate "/etc/nginx/ssl/webhook_fullchain.pem";
    ssl_certificate_key "/etc/nginx/ssl/webhook_privkey.key";
    ssl_trusted_certificate "/etc/nginx/ssl/webhook_fullchain.pem";

    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    proxy_intercept_errors on;
    error_page 400 404 500 502 @redirect;

    location / {
        proxy_pass http://remnawave-minishop$request_uri;
    }

    location @redirect {
        return 404;
    }
}

Для Web App используйте отдельный upstream на WEBAPP_SERVER_PORT; пример есть в webapp.md.

SSL для домена вебхуков

Пример выпуска сертификата через acme.sh:

sudo apt-get install cron socat
curl https://get.acme.sh | sh -s email=EMAIL
source ~/.bashrc
ufw allow 80/tcp
ufw reload

acme.sh --set-default-ca --server letsencrypt
acme.sh --issue --standalone -d 'webhook.domain.com' \
  --key-file /opt/remnawave/nginx/webhook_privkey.key \
  --fullchain-file /opt/remnawave/nginx/webhook_fullchain.pem

Если Nginx панели Remnawave запускается в Docker, добавьте сертификаты в volumes сервиса Nginx:

services:
  remnawave-nginx:
    volumes:
      - ./webhook_fullchain.pem:/etc/nginx/ssl/webhook_fullchain.pem:ro
      - ./webhook_privkey.key:/etc/nginx/ssl/webhook_privkey.key:ro

После изменения конфигурации перезапустите Nginx:

cd /opt/remnawave/nginx
docker compose down
docker compose up -d
docker compose logs -f -t

Caddy

Для схемы с Caddy используйте docker-compose-caddy.yml и Caddyfile. Caddy публикует наружу 80 и 443, выпускает TLS-сертификаты и проксирует вебхуки и Web App на разные внутренние порты.

Пример Caddyfile:

webhook.domain.com {
    encode zstd gzip
    reverse_proxy remnawave-minishop:{$WEB_SERVER_PORT:8080}
}

app.domain.com {
    encode zstd gzip
    reverse_proxy remnawave-minishop:{$WEBAPP_SERVER_PORT:8081}
}

В .env укажите:

WEBHOOK_BASE_URL=https://webhook.domain.com
SUBSCRIPTION_MINI_APP_URL=https://app.domain.com/

Запуск:

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

В BotFather укажите домен Mini App через настройки домена, чтобы он совпадал с SUBSCRIPTION_MINI_APP_URL.

Проверка

docker compose ps
docker compose logs -f remnawave-minishop

Проверьте:

  • бот отвечает в Telegram;
  • Telegram webhook установлен без ошибок в логах;
  • платежные вебхуки доходят до приложения;
  • Remnawave webhook проходит проверку PANEL_WEBHOOK_SECRET;
  • Web App открывается по домену из SUBSCRIPTION_MINI_APP_URL;
  • в BotFather разрешены URL Web App и callback.