--- description: "Разверните SnapOtter в продакшене с помощью Docker. Требования к оборудованию, настройка GPU и конфигурации обратного прокси для Nginx, Traefik и Cloudflare." i18n_source_hash: 6b6957060fa6 i18n_provenance: machine i18n_output_hash: 8992ab5d785b --- # Развёртывание {#deployment} SnapOtter развёртывается как стек Docker Compose из 3 контейнеров: образ приложения SnapOtter, PostgreSQL 17 и Redis 8. Образ приложения поддерживает **linux/amd64** (с NVIDIA CUDA для ускорения ИИ) и **linux/arm64** (CPU), поэтому он работает нативно на серверах Intel/AMD, компьютерах Mac на Apple Silicon и устройствах ARM, таких как Raspberry Pi 4/5. Ускорение через iGPU Intel/AMD посредством VA-API, Quick Sync или OpenCL для ИИ-инференса сегодня не поддерживается. См. [Docker Image](./docker-tags) для настройки GPU, примеров Docker Compose и закрепления версий. ## Быстрый старт (CPU) {#quick-start-cpu} ```yaml # docker-compose.yml - Copy this file and run: docker compose up -d services: SnapOtter: image: snapotter/snapotter:latest # or ghcr.io/snapotter-hq/snapotter:latest container_name: SnapOtter ports: - "1349:1349" # Web UI + API volumes: - SnapOtter-data:/data # AI models, user files (PERSISTENT) - SnapOtter-workspace:/tmp/workspace # Temp processing files (can be tmpfs) environment: # --- Authentication --- - AUTH_ENABLED=true # Set to false to disable login entirely - DEFAULT_USERNAME=admin # First-run admin username - DEFAULT_PASSWORD=admin # First-run admin password (you'll be forced to change it) # --- Database + Queue --- - DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter - REDIS_URL=redis://redis:6379 # --- Limits (set 0 for unlimited) --- # - MAX_UPLOAD_SIZE_MB=100 # Per-file upload limit in MB # - MAX_BATCH_SIZE=100 # Max files per batch request # - RATE_LIMIT_PER_MIN=1000 # API rate limit per IP, default shown (0 = disabled) # - MAX_USERS=0 # Max user accounts # --- Networking --- # - TRUST_PROXY=true # Trust X-Forwarded-For headers (set false if not behind a proxy) # --- Bind mount permissions --- # - PUID=1000 # Match your host user's UID (run: id -u) # - PGID=1000 # Match your host user's GID (run: id -g) depends_on: postgres: condition: service_healthy redis: condition: service_healthy restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:1349/api/v1/health"] interval: 30s timeout: 5s start_period: 60s retries: 3 shm_size: "2gb" # Needed for Python ML shared memory logging: driver: json-file options: max-size: "10m" max-file: "3" postgres: image: postgres:17-alpine container_name: SnapOtter-postgres environment: POSTGRES_USER: snapotter POSTGRES_PASSWORD: snapotter # Change this for non-local deployments POSTGRES_DB: snapotter volumes: - SnapOtter-pgdata:/var/lib/postgresql/data restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U snapotter"] interval: 10s timeout: 5s retries: 12 start_period: 15s redis: image: redis:8-alpine container_name: SnapOtter-redis command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"] volumes: - SnapOtter-redisdata:/data restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 12 start_period: 10s volumes: SnapOtter-data: # Named volume - Docker manages permissions automatically SnapOtter-workspace: SnapOtter-pgdata: SnapOtter-redisdata: ``` ```bash docker compose up -d ``` После этого приложение доступно по адресу `http://localhost:1349`. > **Ограничения по частоте запросов Docker Hub?** Замените `snapotter/snapotter:latest` на `ghcr.io/snapotter-hq/snapotter:latest`, чтобы загружать образ из GitHub Container Registry. Оба реестра получают один и тот же образ при каждом релизе. ## Быстрый старт (NVIDIA CUDA) {#quick-start-nvidia-cuda} Для ускорения NVIDIA CUDA на ИИ-инструментах (удаление фона, апскейлинг, улучшение лиц, OCR): ```yaml # docker-compose-gpu.yml - Requires: NVIDIA GPU + nvidia-container-toolkit # Install toolkit: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html services: SnapOtter: image: snapotter/snapotter:latest container_name: SnapOtter ports: - "1349:1349" volumes: - SnapOtter-data:/data - SnapOtter-workspace:/tmp/workspace environment: - AUTH_ENABLED=true - DEFAULT_USERNAME=admin - DEFAULT_PASSWORD=admin - DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter - REDIS_URL=redis://redis:6379 depends_on: postgres: condition: service_healthy redis: condition: service_healthy restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:1349/api/v1/health"] interval: 30s timeout: 5s start_period: 60s retries: 3 shm_size: "2gb" # Required for PyTorch CUDA shared memory deploy: resources: reservations: devices: - driver: nvidia count: all # Or set to 1 for a specific GPU capabilities: [gpu] logging: driver: json-file options: max-size: "10m" max-file: "3" postgres: image: postgres:17-alpine container_name: SnapOtter-postgres environment: POSTGRES_USER: snapotter POSTGRES_PASSWORD: snapotter POSTGRES_DB: snapotter volumes: - SnapOtter-pgdata:/var/lib/postgresql/data restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U snapotter"] interval: 10s timeout: 5s retries: 12 start_period: 15s redis: image: redis:8-alpine container_name: SnapOtter-redis command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"] volumes: - SnapOtter-redisdata:/data restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 12 start_period: 10s volumes: SnapOtter-data: SnapOtter-workspace: SnapOtter-pgdata: SnapOtter-redisdata: ``` ```bash docker compose -f docker-compose-gpu.yml up -d ``` Проверьте обнаружение CUDA в логах: ```bash docker logs SnapOtter 2>&1 | head -20 # Look for: [gpu] CUDA available via torch ``` ## Требования к оборудованию {#hardware-requirements} Эти цифры получены из бенчмарков на разных системах: от современной рабочей станции amd64 с NVIDIA RTX 4070 до Raspberry Pi. На каждой прогонялся весь каталог инструментов, а лимиты ресурсов Docker варьировались, чтобы найти реальный минимум. ### Краткая справка {#quick-reference} | Уровень | Сценарий использования | CPU | RAM | GPU | Хранилище | |------|----------|-----|-----|-----|---------| | Минимум | Инструменты для изображений, файлов и лёгкие PDF-инструменты; один пользователь; небольшие пакеты | 2 ядра | 2 ГБ | Нет | ~7 ГБ | | Рекомендуется | Все пять модальностей, включая видео, PDF и ИИ на CPU; пакеты; несколько пользователей | 4 ядра | 4 ГБ | Нет | ~25 ГБ | | Полный | Всё на скорости, включая ИИ на GPU; большие пакеты; много пользователей | 6-8 ядер | 8 ГБ | NVIDIA 8 ГБ+ VRAM (12 ГБ комфортно) | ~35 ГБ | **Архитектура: только 64-битная** (`linux/amd64` или `linux/arm64`). SnapOtter работает нативно на серверах Intel/AMD, компьютерах Mac на Apple Silicon и 64-битных платах ARM, включая **Raspberry Pi 4 и 5** (4-8 ГБ). Он **не** работает на 32-битном ARM (`armv7`/`armhf`), так как образ для него не собирается, а также на платах класса 512 МБ, таких как Pi Zero, которые ниже минимального объёма памяти (см. ниже). ### Минимум (инструменты для изображений, файлов и лёгкие PDF-инструменты; без ИИ) {#minimum-image-files-and-light-pdf-tools-no-ai} | Ресурс | Требование | |---|---| | CPU | 2 ядра | | RAM | 2 ГБ | | Диск | ~5,5 ГБ (образ) + том данных | | GPU | Не требуется | Все 222 инструмента каталога без ИИ: изображения (изменение размера, обрезка, конвертация, сжатие, коррекция, водяной знак), видео (обрезка, отключение звука, ремукс), аудио (конвертация, нормализация, обрезка), PDF (объединение, разделение, сжатие, поворот, защита), конвертации файлов и специальные пресеты конвертации, работают на скромном оборудовании. Большинство операций завершаются заметно быстрее секунды даже на большом файле: изображение размером 2,7 МБ изменяется в размере за ~0,05 с и перекодируется в WebP за ~2 с. Минимальный объём памяти реален, по результатам перебора лимитов ресурсов Docker: **512 МБ не позволяют запустить стек** (даже одно изменение размера изображения завершается принудительно), **1 ГБ** справляется с операциями над одним файлом, но пакет из нескольких файлов исчерпывает память, а **2 ГБ / 2 ядра** это наименьшая конфигурация, которая комфортно обрабатывает пакеты. ```yaml deploy: resources: limits: cpus: '2' memory: 2G ``` **Единственное исключение, требовательное к CPU, это перекодирование видео.** Операции с копированием потока (обрезка, отключение звука, ремукс контейнера) мгновенны, но транскодирование в другой кодек упирается в CPU. Клип 1080p длительностью 45 секунд, перекодированный в VP9 (WebM), занимает примерно **~40 с** на быстром современном CPU, ~45 с на Apple Silicon, ~80 с на более старом мобильном 4-ядерном процессоре и **~130 с** на более старом 4-ядерном сервере. Если ваша рабочая нагрузка ориентирована на видео, отдавайте приоритет числу и тактовой частоте ядер CPU или повысьте лимит `cpus:` контейнера: поставляемый compose по умолчанию ограничивает приложение 4 ядрами (8 в compose для GPU). ### Рекомендуется (ИИ-инструменты на CPU) {#recommended-ai-tools-on-cpu} | Ресурс | Требование | |---|---| | CPU | 4 ядра | | RAM | 4 ГБ | | Диск | 3 ГБ (образ) + 24 ГБ (модели ИИ) + рабочее пространство | | GPU | Не требуется (запасной вариант на CPU) | **Именно установка ИИ-пакетов повышает требования к RAM до 4 ГБ.** Без установленного ИИ приложение простаивает примерно на 360 МБ; со всеми семью установленными пакетами оно удерживает ~2,6 ГБ в резиденции, потому что Python-сайдкар ИИ предзагружает свои модели (удаление фона, апскейлинг, OCR, транскрипция, обнаружение лиц, реставрация) при запуске. Установки без ИИ остаются лёгкими; установки с ИИ требуют ≥4 ГБ. Большинство ИИ-инструментов вполне пригодны на CPU; пара действительно требует GPU. Измерено на современном 4-ядерном CPU: | ИИ-инструмент | Время на CPU | Пригоден на CPU? | |---|---|---| | Обнаружение лиц (размытие лиц, умная обрезка, красные глаза), удаление шума | менее 1 с | Да | | OCR, транскрипция, субтитры | 1-3 с | Да | | Раскрашивание, улучшение лиц | ~10 с | Да | | Удаление / замена / размытие фона | ~29 с | Да (придётся подождать) | | ИИ-апскейл (RealESRGAN) | ~33 с для малых; минуты для больших изображений | Условно, настоятельно рекомендуется GPU | | Реставрация фото (полный конвейер) | несколько минут | Нет, требуется GPU или быстрый многоядерный CPU | SnapOtter намеренно не встраивает загрузки этих моделей в образ Docker. ИИ-пакеты загружаются только тогда, когда администратор включает соответствующий инструмент, хранятся в постоянном томе `/data/ai` и совместно используются каждым инструментом, зависящим от одного и того же набора моделей. Это сохраняет итоговый образ контейнера небольшим и в то же время позволяет полной установке ИИ достичь бо́льших цифр по хранилищу, указанных ниже. Некоторые инструменты зависят от более чем одного общего пакета. Например, для инструмента Passport Photo требуются оба пакета `background-removal` и `face-detection`; если `background-removal` уже установлен, включение Passport Photo загружает только недостающий пакет `face-detection`. То же повторное использование применяется ко всем ИИ-инструментам. Размеры загрузок моделей ИИ: | Пакет | Размер на диске | |---|---| | Удаление фона | 4-5 ГБ | | Апскейл + Улучшение лиц + Удаление шума | 5-6 ГБ | | Обнаружение лиц | 200-300 МБ | | Ластик объектов + Раскрашивание | 1-2 ГБ | | OCR | 5-6 ГБ | | Реставрация фото | 4-5 ГБ | | **Все пакеты** | **~24 ГБ** | ```yaml deploy: resources: limits: cpus: '4' memory: 4G ``` ### Полный (ИИ-инструменты на NVIDIA CUDA) {#full-ai-tools-on-nvidia-cuda} | Ресурс | Требование | |---|---| | CPU | 6-8 ядер (подготовка видео + параллелизм выполняются на CPU даже при ИИ на GPU) | | RAM | 8 ГБ | | GPU | NVIDIA с 8+ ГБ VRAM (рекомендуется 12 ГБ) | | Диск | ~35 ГБ всего | GPU NVIDIA (CUDA) резко ускоряет тяжёлые модели ИИ. Измерено на RTX 4070 в сравнении с современным CPU: | ИИ-инструмент | Ускорение с GPU | Примечания | |---|---|---| | ИИ-апскейл (RealESRGAN 2×) | **~47×** | Самый большой выигрыш: менее секунды против ~33 с (минуты для больших изображений) | | Улучшение лиц (CodeFormer) | **~12×** | ~0,9 с против ~11 с | | Транскрипция (Whisper) | ~4,5× | | | Удаление / замена / размытие фона | ~4× | ~7 с на GPU против ~29 с на CPU | | Раскрашивание | ~1,8× | | | OCR, обнаружение лиц, красные глаза, удаление шума | ~1× | Уже быстро на CPU, GPU не помогает | | Реставрация фото | нет | Упирается в CPU даже на GPU (0% загрузки GPU); быстрый CPU здесь важнее GPU | Инструменты, которым стоит выделить GPU, это **апскейл, улучшение лиц, транскрипция и удаление фона**. Обнаружение лиц, OCR и красные глаза упираются в CPU и уже быстры, поэтому GPU ничего не добавляет. Пиковое использование VRAM достигает 7,5 ГБ во время апскейла с улучшением лиц. GPU NVIDIA на 6 ГБ работает для большинства ИИ-инструментов по отдельности, но не справится с апскейлом. 8-12 ГБ VRAM справляются со всем. Ускорение через iGPU Intel/AMD посредством VA-API, Quick Sync или OpenCL для ИИ-инференса сегодня не поддерживается. Проброс `/dev/dri` в контейнер не включает ускорение ИИ на GPU; SnapOtter будет запускать ИИ-инструменты на CPU, если недоступна NVIDIA CUDA. ```yaml deploy: resources: limits: cpus: '4' memory: 8G reservations: devices: - driver: nvidia count: all capabilities: [gpu] ``` ### Одновременные пользователи {#concurrent-users} Параллельные запросы на изменение размера изображения к контейнеру приложения с ограничением по умолчанию в 4 ядра: | Одновременные запросы | Среднее время ответа | Ошибки | |---|---|---| | 1 | 0,4 с | 0 | | 5 | 1,2 с | 0 | | 10 | 2,1 с | 0 | Время ответа ухудшается сублинейно без ошибок по мере насыщения пула воркеров. Повышение лимита `cpus:` контейнера приложения (или использование хоста с бо́льшим числом ядер) поднимает потолок. Обратите внимание, что тяжёлые задания (транскодирование видео, ИИ на CPU) удерживают воркер на всю свою продолжительность, поэтому подбирайте CPU под ожидаемое число одновременных тяжёлых заданий, а не только под число запросов. ### Поддерживаемые форматы изображений {#supported-image-formats} SnapOtter поддерживает **55+ входных форматов** и **14 выходных форматов**, включая RAW-файлы от 20+ марок камер, профессиональные форматы (PSD, EPS, OpenEXR, HDR), современные кодеки (JPEG XL, AVIF, HEIC, QOI) и научные/игровые форматы (FITS, DDS). Подробности о каждом поддерживаемом формате, используемом декодере и доступных настройках качества см. в [полном списке форматов](/ru/guide/supported-formats). ### Известные ограничения {#known-limitations} - **Изменение размера с учётом содержимого** аварийно завершается на больших изображениях (>5 Мп) из-за ограничения в бинарнике caire. Нормально работает с изображениями меньшего размера. - **Декодирование HEIF** занимает 13-23 секунды. HEIC (вариант Apple) намного быстрее: 0,3-0,9 секунды. - **OCR для японского** не работает на CPU из-за ошибки MKLDNN в PaddlePaddle. Работает на GPU. - **Апскейл** превышает тайм-аут на CPU для всего, кроме малых изображений. Для практического использования требуется GPU. - Улучшение лиц **CodeFormer** значительно медленнее, чем GFPGAN (53 с против 2 с на GPU). Для большинства случаев рекомендуется GFPGAN. ## Тома {#volumes} | Монтирование / Том | Назначение | Обязательно? | |---|---|---| | `/data` (app) | Модели ИИ, Python venv, пользовательские файлы | **Да**, без него потеря файлов | | `/tmp/workspace` (app) | Временные файлы обработки (очищаются автоматически) | Рекомендуется | | `SnapOtter-pgdata` (postgres) | Каталог данных PostgreSQL (пользователи, настройки, конвейеры, задания) | **Да**, без него потеря данных | | `SnapOtter-redisdata` (redis) | Append-only файл Redis для устойчивых очередей заданий | Рекомендуется | ### Bind-монтирования против именованных томов {#bind-mounts-vs-named-volumes} **Именованные тома** (рекомендуется): Docker управляет правами доступа автоматически: ```yaml volumes: - SnapOtter-data:/data ``` **Bind-монтирования**: правами управляете вы. Установите `PUID`/`PGID` в соответствии с вашим пользователем на хосте: ```yaml volumes: - ./SnapOtter-data:/data environment: - PUID=1000 # Your host UID (run: id -u) - PGID=1000 # Your host GID (run: id -g) ``` ### Права доступа к хранилищу {#storage-permissions} SnapOtter записывает в два места во время работы: `/data` (пользовательские файлы, логи, модели ИИ и Python venv) и `/tmp/workspace` (временная рабочая область обработки). Оба должны быть доступны для записи пользователю, от имени которого запущен контейнер. Если это не так, контейнер **быстро завершается с ошибкой при запуске**, выводя сообщение с именем каталога, текущим UID/GID и способом устранения, вместо того чтобы стартовать «здоровым» и затем упасть на первой же загрузке с непонятной ошибкой. Способ обработки прав доступа зависит от того, как запускается контейнер: **По умолчанию (запускается как root, понижается до `snapotter`):** точка входа запускается как root, исправляет владение смонтированными томами, затем понижается до непривилегированного пользователя `snapotter` через `gosu`. Именованные тома работают без настройки. Для bind-монтирований установите `PUID`/`PGID` на вашего пользователя на хосте (выше), чтобы записываемые файлы принадлежали вам. **Kubernetes / OpenShift (не-root через `runAsUser`):** запускаясь напрямую от имени не-root пользователя, контейнер не может сам сменить владельца томов, поэтому оркестратор должен сделать их доступными для записи. Установите `fsGroup`: ```yaml securityContext: runAsUser: 999 runAsGroup: 999 fsGroup: 999 # makes mounted volumes writable by the pod ``` Записываемые каталоги образа принадлежат группе GID 0 и доступны для записи группе, поэтому под, работающий с **произвольным UID** плюс дополнительной корневой группой (по умолчанию в OpenShift), может записывать без `chown`. **TrueNAS Scale (и другие настройки с «чужим UID»):** TrueNAS запускает приложения от имени не-root пользователя (часто `568:568`) и монтирует наборы данных хоста, принадлежащие другому пользователю, поэтому ни точка входа, ни `fsGroup` не делают их доступными для записи самостоятельно. Выберите один вариант: - **Запустить приложение как root** (рекомендуется): оставьте пользователя приложения не заданным или установите его в `0` и позвольте точке входа по умолчанию исправить права и понизиться до `snapotter`. - **Запустить как UID `999`**: установите пользователя/группу приложения в `999:999` (встроенный пользователь `snapotter` SnapOtter), чтобы он соответствовал владению образа. - **`chown` набор данных хоста** на UID, от имени которого работает контейнер, из оболочки TrueNAS: ```bash # Используйте UID из ошибки при запуске (или выполните `id` внутри контейнера) chown -R 568:568 /mnt// ``` Ошибка при запуске называет точный UID для использования, поэтому самый быстрый путь такой: запустить приложение один раз, прочитать сообщение, затем выполнить `chown` (или изменить пользователя) соответственно. ## Переменные окружения {#environment-variables} | Переменная | По умолчанию | Описание | |---|---|---| | `AUTH_ENABLED` | `true` | Включить/отключить требование входа | | `DEFAULT_USERNAME` | `admin` | Начальное имя администратора | | `DEFAULT_PASSWORD` | `admin` | Начальный пароль администратора (принудительная смена при первом входе) | | `MAX_UPLOAD_SIZE_MB` | `100` | Лимит загрузки на файл | | `MAX_BATCH_SIZE` | `100` | Макс. число файлов на пакетный запрос | | `RATE_LIMIT_PER_MIN` | `1000` | Запросов API в минуту на IP (установите 0 для отключения) | | `MAX_USERS` | `0` (без ограничений) | Максимальное число учётных записей пользователей | | `TRUST_PROXY` | `true` | Доверять заголовкам X-Forwarded-For от обратного прокси | | `PUID` | `999` | Запускать с этим UID (для прав bind-монтирований) | | `PGID` | `999` | Запускать с этим GID (для прав bind-монтирований) | | `LOG_LEVEL` | `info` | Уровень логирования: fatal, error, warn, info, debug, trace | | `CONCURRENT_JOBS` | `0` (авто) | Макс. число параллельных ИИ-заданий обработки | | `SESSION_DURATION_HOURS` | `168` | Время жизни сессии входа (7 дней) | | `CORS_ORIGIN` | (пусто) | Разрешённые источники через запятую, или пусто для same-origin | ## Проверка работоспособности {#health-check} Контейнер включает встроенную проверку работоспособности: ```bash # Check container health status docker inspect --format='{{.State.Health.Status}}' SnapOtter # Manual health check curl http://localhost:1349/api/v1/health # {"status":"healthy","version":"x.y.z"} ``` ## Обратный прокси {#reverse-proxy} SnapOtter устанавливает `TRUST_PROXY=true` по умолчанию, чтобы ограничение частоты запросов и логирование использовали реальный IP клиента из заголовков `X-Forwarded-For`. ### Nginx {#nginx} ```nginx server { listen 80; server_name images.example.com; # Match MAX_UPLOAD_SIZE_MB (0 = nginx default 1M, so set high for unlimited) client_max_body_size 500M; location / { proxy_pass http://localhost:1349; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 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; # SSE support (batch progress, feature install progress) proxy_buffering off; proxy_read_timeout 300s; } } ``` ### Nginx Proxy Manager {#nginx-proxy-manager} 1. Добавьте новый Proxy Host 2. Установите Domain Name на ваш домен 3. Установите Scheme на `http`, Forward Hostname на `SnapOtter` (или IP вашего контейнера), Forward Port на `1349` 4. Включите поддержку WebSocket 5. В разделе Advanced добавьте: `client_max_body_size 500M;` и `proxy_buffering off;` ### Traefik {#traefik} ```yaml # Add these labels to the SnapOtter service in docker-compose.yml labels: - "traefik.enable=true" - "traefik.http.routers.snapotter.rule=Host(`images.example.com`)" - "traefik.http.routers.snapotter.entrypoints=websecure" - "traefik.http.routers.snapotter.tls.certresolver=letsencrypt" - "traefik.http.services.snapotter.loadbalancer.server.port=1349" # Increase upload limit (default 2MB is too low) - "traefik.http.middlewares.snapotter-body.buffering.maxRequestBodyBytes=524288000" - "traefik.http.routers.snapotter.middlewares=snapotter-body" ``` ### Caddy {#caddy} ```txt images.example.com { reverse_proxy localhost:1349 { flush_interval -1 transport http { read_timeout 300s write_timeout 300s } } } ``` `flush_interval -1` отключает буферизацию ответа, что необходимо для событий прогресса SSE (пакетная обработка, ИИ-инструменты, установка функций). Увеличенные тайм-ауты позволяют завершать загрузки больших файлов без того, чтобы Caddy закрывал соединение раньше времени. ### Cloudflare Tunnels {#cloudflare-tunnels} ```bash cloudflared tunnel --url http://localhost:1349 ``` Примечание: у Cloudflare лимит загрузки 100 МБ на бесплатных тарифах. Установите `MAX_UPLOAD_SIZE_MB=100` в соответствии с этим. ## CI/CD {#ci-cd} В репозитории GitHub есть три рабочих процесса: - **ci.yml**: запускается автоматически при каждом push и PR. Линтит, проверяет типы, тестирует, собирает и валидирует образ Docker (без push). - **release.yml**: запускается вручную через `workflow_dispatch`. Выполняет semantic-release для создания тега версии и релиза GitHub, затем собирает мультиархитектурный образ Docker (amd64 + arm64) и отправляет его в Docker Hub (`snapotter/snapotter`) и GitHub Container Registry (`ghcr.io/snapotter-hq/snapotter`). - **deploy-docs.yml**: собирает этот сайт документации и разворачивает его на Cloudflare Pages при push в `main`. Чтобы создать релиз, перейдите в **Actions > Release > Run workflow** в интерфейсе GitHub или выполните: ```bash gh workflow run release.yml ``` Semantic-release определяет версию по истории коммитов. Тег Docker `latest` всегда указывает на самый последний релиз. ## Аналитика {#analytics} SnapOtter включает анонимную продуктовую аналитику (паттерны использования инструментов, отчёты об ошибках), чтобы помогать отлавливать баги и улучшать функции. Она включена по умолчанию. Ваши файлы, имена файлов и персональные данные никогда не являются её частью. SnapOtter работает нормально с отключённой аналитикой. ### Отключение аналитики {#disabling-analytics} Отказ во время работы делается переключателем для администратора в один клик. Откройте Settings > System > Privacy и отключите Anonymous Product Analytics. Она останавливается немедленно для всего экземпляра, пересборка не требуется. Для образа, который никогда не сможет отправлять аналитику, задайте жёсткое отключение на этапе сборки, клонировав репозиторий и пересобрав его: ```bash git clone https://github.com/snapotter-hq/SnapOtter.git cd SnapOtter docker compose -f docker/docker-compose.yml build --build-arg SNAPOTTER_ANALYTICS=off docker compose -f docker/docker-compose.yml up -d ``` Либо добавьте аргумент сборки в существующий `docker-compose.yml`: ```yaml services: snapotter: build: context: . dockerfile: docker/Dockerfile args: SNAPOTTER_ANALYTICS: "off" ```