ДОКУМЕНТАЦИЯ

УСТАНОВКА · § 010

Установка сервера
через Docker Compose

Поднимем backend, admin-панель, магазин, PostgreSQL и Redis одной командой. В конце у тебя будет работающий магазин и панель по доменам.

~10 минутDocker + Docker ComposeVPS 1 vCPU / 1 GB RAM

§ 010.01

Что понадобится

Минимальные требования для production

СЕРВЕР

VPS с Linux

от 1 vCPU / 1 GB RAM / 10 GB SSD

DOCKER

Docker Engine + Compose

24.x+ / v2.20+

ДОМЕН

Доменное имя

3 поддомена: api, panel, shop

ПЛАТЕЖИ

Аккаунт YooKassa, Heleket или другой платёжной системы

нужно для приёма оплаты

§ 010.02

Шаг 01 — Поставить Docker на сервер

Если Docker уже стоит - пропускай

Самый простой способ на Ubuntu/Debian — официальный скрипт от Docker:

SHELL

curl -fsSL https://get.docker.com | sh

※ Проверка установки: docker --version

§ 010.03

Шаг 02 — Создать docker-compose.yml

Один файл на все 5 сервисов: backend, panel, shop, PostgreSQL и Redis

Создай пустую папку под проект и положи в неё файл docker-compose.yml со следующим содержимым:

docker-compose.yml

services:
  backend:
    image: ghcr.io/fresh-donate/backend:latest
    pull_policy: always
    restart: unless-stopped
    env_file: [.env]
    volumes:
      - uploads_data:/app/uploads
    depends_on:
      postgres: { condition: service_healthy }
      redis:    { condition: service_healthy }
    networks: [fresh-network]

  panel:
    image: ghcr.io/fresh-donate/panel:latest
    pull_policy: always
    restart: unless-stopped
    env_file: [.env]
    depends_on: [backend]
    networks: [fresh-network]

  shop:
    image: ghcr.io/fresh-donate/shop:latest
    pull_policy: always
    restart: unless-stopped
    env_file: [.env]
    depends_on: [backend]
    networks: [fresh-network]

  postgres:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${DB_NAME:-fresh_donate}
      POSTGRES_USER: ${DB_USER:-postgres}
      POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-fresh_donate}"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks: [fresh-network]

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD:-redis}
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-redis}", "ping"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks: [fresh-network]

volumes:
  postgres_data:
  redis_data:
  uploads_data:

networks:
  fresh-network:
    driver: bridge

※ Образы ghcr.io/fresh-donate/* публикуются автоматически из main-ветки каждого репозитория. pull_policy: always заставит Compose качать свежий образ при каждом up.

§ 010.04

Шаг 03 — Создать .env

Все секреты и публичные URL — здесь

Рядом с docker-compose.yml создай файл .env со следующим содержимым. Это полный набор переменных — закомментированные значения можно оставить как есть, секреты обязательно заменить.

.env

# === Frontend (panel + shop) ===
# Куда фронтенды ходят за API. Домен на котором стоит бекенд.
NUXT_PUBLIC_API_BASE=https://api.your-domain.com

# === Backend Server ===
HOST=0.0.0.0
PORT=3001
SERVER_PORT=3001

# === Database ===
DB_HOST=postgres
DB_PORT=5432
DB_NAME=fresh_donate
DB_USER=postgres
DB_PASSWORD=сложный-пароль-postgres

# === Redis ===
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=сложный-пароль-redis

# === Admin Panel Credentials ===
ADMIN_LOGIN=admin
ADMIN_PASSWORD=сильный-пароль-минимум-16-символов

# === JWT ===
# Любая случайная строка ≥32 символов. Сгенерировать: openssl rand -hex 32
JWT_SECRET=замени-на-длинную-случайную-строку
JWT_EXPIRES_IN=7d

# === Rate Limiting ===
RATE_LIMIT_MAX=100
RATE_LIMIT_TIMEWINDOW=60000

# === Payment ===
# Куда возвращается игрок после оплаты (фронтенд магазина)
PAYMENT_RETURN_URL=https://shop.your-domain.com
# Публичный URL backend для вебхуков платёжных шлюзов
WEBHOOK_BASE_URL=https://api.your-domain.com
# Раскомментируй, если backend за прокси без x-forwarded-for или в тестовом режиме
# YOOKASSA_SKIP_IP_CHECK=true

# === CORS (через запятую) ===
CORS_ORIGIN=https://panel.your-domain.com,https://shop.your-domain.com

Важно

Не оставляй на проде дефолтные пароли БД/Redis, admin/admin и пустой JWT_SECRET. Любой, кто угадает JWT_SECRET, сможет выпустить себе токен админа.

DB_HOST=postgres и REDIS_HOST=redis — это имена сервисов из docker-compose.yml. Внутри docker-сети контейнеры обращаются друг к другу по этим именам, а не по localhost.

§ 010.05

Шаг 04 — Запуск

Одной командой

SHELL

docker compose up -d

Compose:

  • · скачает свежие образы backend / panel / shop с ghcr.io;
  • · поднимет PostgreSQL 17 и Redis 7 с healthcheck;
  • · при первом старте создаст все таблицы автоматически (sequelize.sync);
  • · запустит сервисы с restart: unless-stopped — они переживут перезагрузку VPS.

Проверь, что контейнеры живы:

SHELL

docker compose ps

docker compose logs -f backend

§ 010.06

Шаг 05 — Reverse proxy и HTTPS

Compose поднимает сервисы во внутренней docker-сети. Наружу их выводит nginx (или любой другой reverse proxy)

Сервисы внутри сети:

backend

:3001

REST API, вебхуки

panel

:3000

Админ-панель

shop

:3002

Витрина для игроков

postgres

:5432

База данных (внутри docker-сети)

redis

:6379

Кэш и rate-limit (внутри docker-сети)

Пример конфига для nginx — три server-блока, по одному на поддомен:

/etc/nginx/sites-available/fresh-donate.conf

# API
server {
    listen 80;
    server_name api.your-domain.com;

    location / {
        proxy_pass http://backend:3001;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        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;
    }
}

# Админ-панель
server {
    listen 80;
    server_name panel.your-domain.com;

    location / {
        proxy_pass http://panel:3000;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        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;
    }
}

# Магазин
server {
    listen 80;
    server_name shop.your-domain.com;

    location / {
        proxy_pass http://shop:3002;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        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;
    }
}

Активируй конфиг и проверь синтаксис:

SHELL

ln -s /etc/nginx/sites-available/fresh-donate.conf /etc/nginx/sites-enabled/

nginx -t # проверка конфига

systemctl reload nginx

HTTPS-сертификаты — через certbot, он сам пропишет 443 и редирект 80→443:

SHELL

certbot --nginx -d api.your-domain.com -d panel.your-domain.com -d shop.your-domain.com

※ Если nginx стоит на хосте (а не в compose), proxy_pass должен указывать на пробросы портов, например http://127.0.0.1:3001. Для этого добавь ports: ["3001:3001"] в нужный сервис compose. Если nginx в compose и в той же сети fresh-network — используй имена сервисов как в примере.

§ 010.07

Все переменные окружения

Полный набор того, что можно настроить через .env

NUXT_PUBLIC_API_BASE

http://localhost:3001

URL backend для фронтендов panel и shop. Подставляется в Nuxt на этапе сборки.

HOST / PORT

0.0.0.0 / 3001

На каком интерфейсе и порту слушает backend внутри контейнера.

SERVER_PORT

3001

Внешний порт хоста, на который пробрасывается backend (если используешь ports вместо reverse-proxy).

DB_HOST / DB_PORT

postgres / 5432

Где живёт PostgreSQL. Внутри compose-сети — имя сервиса postgres.

DB_NAME / DB_USER / DB_PASSWORD

fresh_donate / postgres / postgres

Подключение к БД. Пароль на проде обязательно сменить.

REDIS_HOST / REDIS_PORT

redis / 6379

Где живёт Redis. Внутри compose-сети — имя сервиса redis.

REDIS_PASSWORD

redis

Пароль Redis. На проде обязательно сменить.

ADMIN_LOGIN / ADMIN_PASSWORD

admin / admin

Учётные данные администратора панели. На проде сменить.

JWT_SECRET

change-me…

Случайная строка ≥32 символов. Используется для подписи токенов. Утечка = доступ админа.

JWT_EXPIRES_IN

7d

Сколько живёт токен админа (7d, 30d, 24h, …).

RATE_LIMIT_MAX

100

Сколько запросов в окно разрешено с одного IP. Окно — RATE_LIMIT_TIMEWINDOW (мс).

RATE_LIMIT_TIMEWINDOW

60000

Длительность окна rate-limit в миллисекундах.

PAYMENT_RETURN_URL

http://localhost:3002

Куда возвращается игрок после оплаты. Должно указывать на shop.

WEBHOOK_BASE_URL

http://localhost:3001

Публичный адрес бекенда — на него платёжные шлюзы шлют вебхуки. На проде обязательно HTTPS.

YOOKASSA_SKIP_IP_CHECK

false

Отключить проверку IP YooKassa. Включать только если backend за прокси без x-forwarded-for или в тестовом режиме.

CORS_ORIGIN

http://localhost:3000,http://localhost:3002

Через запятую — адреса панели и магазина. На проде укажи свои домены.

※ Полный список — в backend/src/config/index.ts и в .env.example.

§ 010.08

Шаг 06 — Проверка

Убедимся, что всё работает

01 -

Открой https://api.your-domain.com/health — должен ответить 200 OK.

02 -

Зайди в https://panel.your-domain.com, войди под ADMIN_LOGIN / ADMIN_PASSWORD. Создай первый товар.

03 -

Открой https://shop.your-domain.com — товар должен появиться в каталоге.

§ 010.09

Что дальше

Сервер поднят — осталось подключить деньги и доставку

※ Что-то не получилось? Напиши в Telegram @zWork1 или открой issue на Github.

FreshDonate