Репозиторий содержит инфраструктуру для запуска Telegram-бота конкурсов и связанного Web App (mini app).
Проект запускается набором контейнеров:
bot— основной Telegram-бот.bg— фоновые задачи (Celery worker + beat).api— HTTP API (FastAPI/uvicorn).front— mini app в production-режиме (статическая сборка, отдаётся через Nginx внутри контейнера на порту 80).redis— брокер и backend для фоновых задач.
Если ваша цель сейчас — быстро поднять бота и адаптировать контент под свой проект, используйте краткий сценарий ниже.
Более подробное техническое ревью и идеи улучшений на будущее вынесены в отдельный файл:
docs/infra-review.md
docker-compose.ymlв корне иsrc/docker/docker-compose.ymlсодержат одинаковый набор сервисов (bot,bg,api,front,redis) сbuild,env_file: .envи host-bind volumes/var/bot/.... Обычно достаточно работать с корневым файлом.
- Docker Engine (с поддержкой
docker composeplugin). - Git.
- Доступ на запись к директориям на хосте:
/var/bot/staticи/var/bot/db.
Проверка:
docker --version
docker compose version- Скопируйте пример переменных в корне репозитория:
cp doc_2026-02-11_13-30-10.env.example .env- Обязательно проверьте/задайте минимум:
BOT_TOKEN=<telegram_bot_token>
BOT_WEBAPP_NAME=<mini_app_short_name_from_@BotFather>
ROOT_ADMIN_TG_IDS=[123456789,987654321]
REDIS_PASS=<strong_password>
REDIS_BROKER_URI=redis://:<REDIS_PASS>@redis:6379/11
REDIS_BROKER_RESULT_BACKEND_URI=redis://:<REDIS_PASS>@redis:6379/12Это не произвольное имя из кода и не username бота.
Это short name вашего Telegram Mini App (часть URL после имени бота):
https://t.me/<bot_username>/<BOT_WEBAPP_NAME>?startapp=...
Как получить/проверить:
- Откройте
@BotFather-> выберите бота. - Команда
/myapps(или/newapp//editappв зависимости от интерфейса). - Найдите ваш Mini App и посмотрите его
short name(slug). - Это значение и нужно записать в
.envкакBOT_WEBAPP_NAME.
Если Mini App не создан, сначала создайте его в BotFather и задайте домен (HTTPS).
- Подготовьте директории данных на VPS (используются как bind mounts):
sudo mkdir -p /var/bot/static /var/bot/db
sudo chown -R $USER:$USER /var/botРекомендуемая команда (сборка + запуск в фоне):
docker compose up -d --buildПроверка состояния:
docker compose psОбычное обновление:
git pull
docker compose up -d --buildПосле изменений:
frontбольше не запускается через Vite dev server — собираетсяyarn buildи отдаётся встроенным Nginx (production-подход).
Обновление с полной пересборкой без кэша (когда нужно «чисто» пересобрать образы):
git pull
docker compose build --no-cache bot bg api front
docker compose up -dLegacy/альтернатива: ручные
docker build -f Dockerfile.* ...обычно не нужны, т.к. текущий compose уже содержитbuildдляbot/bg/api/front.
Безопасный перезапуск (данные в /var/bot/... сохраняются):
docker compose down
docker compose up -dЛоги сервиса:
docker compose logs -f --tail=200 <service>Примеры:
docker compose logs -f --tail=200 bot
docker compose logs -f --tail=200 apiПроверка env внутри контейнера:
docker compose exec -T <service> env | sort | grep -E "BOT_TOKEN|ROOT_ADMIN|REDIS"Пример:
docker compose exec -T bot env | sort | grep -E "BOT_TOKEN|ROOT_ADMIN|REDIS"Обычная остановка (safe):
docker compose downПолный сброс окружения (destructive):
docker compose down -vdown -v удаляет volumes/docker-тома проекта. Если вы храните критичные данные в Docker volumes, они будут потеряны. Для этого проекта основные данные вынесены в bind mounts (/var/bot/static, /var/bot/db), но команду всё равно используйте только осознанно.
- Бот отвечает в Telegram.
- Админ с ID из
ROOT_ADMIN_TG_IDSимеет доступ к админ-функциям. - API отвечает (напрямую или через прокси).
- Mini app открывается.
- Фоновые задачи выполняются без ошибок.
Важно: в проекте уже есть Nginx внутри контейнера front, он раздаёт статику mini app.
Хостовый Nginx (устанавливается на VPS) нужен отдельно только для домена/HTTPS (443) и проксирования к контейнерам.
В nginx.conf есть пример:
/->http://localhost:3000/(mini app, Nginx-контейнер front)/bot/api/->http://localhost:8000/api/(backend API)
Что сделать:
- Положить конфиг в
/etc/nginx/sites-available/<your-domain>.conf. - Проверить
server_nameи пути до TLS-сертификатов. - Включить сайт симлинком в
sites-enabled. - Проверить конфиг:
sudo nginx -t. - Перезапустить Nginx:
sudo systemctl reload nginx.
Для production-конфига из этого репозитория
frontслушает HTTP на порту 80 внутри контейнера и публикуется как3000:80, поэтому upstream прокси:http://localhost:3000/.
Базовый сценарий обновления:
git pull
docker compose up -d --buildПри необходимости очистите неиспользуемые образы:
docker image prune -fLegacy: ручные
docker build -f Dockerfile.* ...оставляйте только как исключение (например, для отладки отдельных образов).
bot/apiпадает сразу:- проверьте заполнение
.env; - проверьте корректность
BOT_TOKEN; - проверьте
REDIS_PASSи Redis URI (hostredis, корректный DB).
- проверьте заполнение
- Нет доступа к Web App/API через домен:
- проверьте
nginx -t; - проверьте TLS-сертификаты и
server_name; - проверьте firewall/security group;
- если видите
ERR_CONNECTION_CLOSED, чаще всего Nginx проксирует mini app на неверный upstream (например,https://localhost:3000вместоhttp://127.0.0.1:3000для Vite в контейнере).
- проверьте
nginx: command not foundилиnginx.service not found:- у вас не установлен Nginx (или используется другой reverse proxy/панель);
- либо установите и настройте Nginx, либо откройте внешний 443/80 на другой прокси (Caddy/Traefik) до
front/api; - проверьте, что контейнер
frontдействительно запущен:docker compose ps front; - проверьте логи фронта:
docker compose logs -f --tail=200 front.
curl -I http://127.0.0.1:3000/возвращаетEmpty reply from server:- обычно фронт отвечает по HTTPS (Vite basic SSL) или процесс фронта не поднялся;
- проверьте протокол:
curl -kI https://127.0.0.1:3000/; - либо отключите HTTPS в Vite и используйте HTTP upstream в прокси (см.
src/miniapp/vite.config.ts).
- Celery не выполняет задачи:
- проверьте логи
bg; - проверьте
REDIS_BROKER_URIиREDIS_BROKER_RESULT_BACKEND_URI.
- проверьте логи
Если позже понадобится, можно добавить отдельный docker-compose.prod.yml с:
build-секциями,- healthcheck’ами,
- отключением публикации внутренних портов наружу.
Ниже команды для Ubuntu/Debian сервера, где домен уже указывает на ваш VPS.
Этот чеклист именно для хостового Nginx.
Nginx внутри front уже есть и дополнительно не ставится — ставим только reverse proxy на самом VPS.
- Установите Nginx и Certbot:
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx- Убедитесь, что DNS уже указывает на сервер:
dig +short contestbotcurling.ru
curl -I http://contestbotcurling.ru- Задеплойте контейнеры проекта:
cd ~/contest-bot
git pull
docker compose up -d --build- Создайте Nginx site config из
nginx.confпроекта:
sudo cp ~/contest-bot/nginx.conf /etc/nginx/sites-available/contestbotcurling.ru.conf
sudo ln -sf /etc/nginx/sites-available/contestbotcurling.ru.conf /etc/nginx/sites-enabled/contestbotcurling.ru.conf
sudo rm -f /etc/nginx/sites-enabled/default- Проверьте Nginx и перезагрузите:
sudo nginx -t
sudo systemctl reload nginx- Выпустите/подключите TLS-сертификат через Certbot:
sudo certbot --nginx -d contestbotcurling.ru -m you@example.com --agree-tos --no-eff-email- Проверка доступности backend/frontend:
curl -I http://127.0.0.1:3000/
curl -I http://127.0.0.1:8000/api/health || true
curl -I https://contestbotcurling.ru/
curl -I https://contestbotcurling.ru/bot/api/- Если что-то не работает — сразу смотрите логи:
sudo journalctl -u nginx -n 200 --no-pager
docker compose logs -f --tail=200 front api botЭто означает, что в системе отключён IPv6, а Nginx пытается слушать IPv6-сокет (listen [::]:80).
Исправление:
# показать где прописан IPv6 listen
rg -n "listen\s+\[::\]" /etc/nginx
# отключить default-site, где чаще всего есть [::]:80
sudo rm -f /etc/nginx/sites-enabled/default
# убедиться, что ваш сайт-конфиг не содержит listen [::]:80
sudo rg -n "listen\s+\[::\]" /etc/nginx/sites-available/contestbotcurling.ru.conf || true
# проверить и запустить nginx
sudo nginx -t
sudo systemctl restart nginxЕсли dpkg прервался на установке, завершите конфигурацию пакетов:
sudo dpkg --configure -a
sudo apt -f install -yПосле этого повторно выполните certbot-команду.
Проверьте, что фронт-контейнер действительно отвечает и отдаёт статические файлы:
docker compose ps front
docker compose logs --tail=200 front
curl -I http://127.0.0.1:3000/
curl -I http://127.0.0.1:3000/check.jpg
curl -I https://contestbotcurling.ru/check.jpgЕсли локально check.jpg открывается, а через домен нет — проблема в host reverse proxy/firewall, а не в miniapp-коде.