A release-readiness QA pass over the whole product. The commits split into defects a user would hit and gates that were reporting green while measuring nothing. ## Fixes that change behaviour Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so request.ip came from a client-set header and a forged X-Forwarded-For got past the login limiter. The default is now a private-network trust list. A transient Postgres outage stranded in-flight jobs, leaving finished output on disk with no row pointing at it. A reconciler now resolves those rows and adopts the bytes rather than dropping the work. A Redis connection that moved to a new address wedged every read-blocked consumer, so completions stopped signalling while health still answered 200. Socket timeouts plus subscriber pings recover it. Installing more than one AI bundle left the shared venv multi-versioned and silently broke three tools. The installer now reconciles distributions to one version each. Converting an image to JXL at quality 1 through 4 returned a 500, because libjxl 0.7 rejects the distance those values compute. The quality is floored at what the encoder honours. A missing ffmpeg was also reported to the user as a corrupt upload; it now says the engine is unavailable. RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at 0.22.2, and the release scan was split so it can fail on an unfixed critical instead of hiding it behind ignore-unfixed. ## Gates that could not fail Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs build; coverage discarded its whole report on any failing test; the lint gate skipped root tests, scripts, and two workspaces; and several generated matrices counted a host missing ffmpeg as a passing tool. Each now measures what it claims. Full evidence and the outstanding release items are tracked locally and are not part of this branch.
47 KiB
description, i18n_source_hash, i18n_provenance, i18n_output_hash, i18n_hash_version
| description | i18n_source_hash | i18n_provenance | i18n_output_hash | i18n_hash_version |
|---|---|---|---|---|
| Розгорніть SnapOtter у продакшені за допомогою Docker. Вимоги до апаратного забезпечення, налаштування GPU та конфігурації зворотного проксі для Nginx, Traefik і Cloudflare. | 2a722f86da75 | human | a466f9f8a47f | 2 |
Розгортання
SnapOtter розгортається як стек Docker Compose із 3 контейнерів: образ застосунку SnapOtter, PostgreSQL 17 і Redis 8. Образ застосунку підтримує linux/amd64 (з NVIDIA CUDA для прискорення AI) і linux/arm64 (CPU), тож він працює нативно на серверах Intel/AMD, на Mac з Apple Silicon і на ARM-пристроях на кшталт Raspberry Pi 4/5. Прискорення на iGPU Intel/AMD через VA-API, Quick Sync або OpenCL наразі не підтримується для AI-інференсу.
Див. Docker Image щодо налаштування GPU, прикладів Docker Compose і фіксації версій.
::: info Сумісність OCR для корейської мови
Швидкий OCR підтримує auto, en, de, es, fr, zh і ja, але не корейську мову (ko). Для корейської потрібен пакет точного OCR і рівень balanced або best. Пакет працює в офіційних контейнерах Linux amd64 і arm64, зокрема на вузлах NVIDIA, де OCR і далі виконується на CPU. Непідтримувані системи отримують явну помилку сумісності без прихованого переходу на fast. Корейська з fast або застарілим псевдонімом tesseract відхиляється до постановки в чергу з FEATURE_INCOMPATIBLE і fast-korean-unsupported.
:::
Швидкий старт (CPU)
# 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=loopback,linklocal,uniquelocal # Which peers may set the client IP via X-Forwarded-For (default shown)
# --- 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 -d 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:
docker compose up -d
Після цього застосунок доступний за адресою http://localhost:1349.
Обмеження швидкості Docker Hub? Замініть
snapotter/snapotter:latestнаghcr.io/snapotter-hq/snapotter:latest, щоб натомість завантажувати з GitHub Container Registry. Обидва реєстри отримують той самий образ при кожному релізі.
Швидкий старт (NVIDIA CUDA)
Для прискорення NVIDIA CUDA у підтримуваних інструментах ШІ (видалення фону, масштабування, покращення обличчя):
# 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 -d 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:
docker compose -f docker-compose-gpu.yml up -d
Перевірте прискорення GPU
Перевірте виявлення CUDA в журналах:
docker logs SnapOtter 2>&1 | head -20
# Look for: [gpu] CUDA available via torch
Якщо інструменти штучного інтелекту працюють на ЦП, навіть якщо --gpus all і NVIDIA Container Toolkit налаштовано правильно, переінсталюйте відповідний комплект (наприклад, Background Removal) із Налаштування → Функції штучного інтелекту. Інсталятор відновлює збірку графічного процесора ONNX Runtime, яку збірка лише для центрального процесора, залучена іншим пакетом (наприклад, транскрипцією), інакше може затіняти в спільному середовищі ШІ. Якщо перевстановлення з інтерфейсу користувача не відновлює GPU на старішому образі, перегляньте посібник із відновлення в випуск №490.
Вимоги до апаратного забезпечення
Ці цифри отримані з бенчмарків на низці систем, від сучасної робочої станції amd64 з NVIDIA RTX 4070 до Raspberry Pi, на яких запускали весь каталог інструментів і перебирали ліміти ресурсів Docker, щоб знайти реальний мінімум.
Працюєте на нижній межі цих рівнів (Pi, старий ноутбук, VPS із 2 GB)? Сторінка Робота на слабкому обладнанні перетворює ці цифри на конкретний покроковий посібник із підібраними лімітами.
Швидка довідка
| Рівень | Сценарій використання | CPU | RAM | GPU | Сховище |
|---|---|---|---|---|---|
| Мінімальний | Інструменти для зображень, файлів і легкі PDF-інструменти; один користувач; невеликі пакети | 2 ядра | 2 GB | Немає | ~7 GB |
| Рекомендований | Усі п'ять модальностей, включно з відео, PDF та AI на CPU; пакети; кілька користувачів | 4 ядра | 4 GB | Немає | ~25 GB |
| Повний | Усе на швидкості, включно з GPU AI; великі пакети; багато користувачів | 6-8 ядер | 8 GB | NVIDIA 8 GB+ VRAM (12 GB комфортно) | ~35 GB |
Архітектура: тільки 64-бітна (linux/amd64 або linux/arm64). SnapOtter працює нативно на серверах Intel/AMD, на Mac з Apple Silicon і на 64-бітних платах ARM, включно з Raspberry Pi 4 і 5 (4-8 GB). Він не працює на 32-бітному ARM (armv7/armhf) — образ для нього не збирається — а також на платах класу 512 МБ, як-от Pi Zero, які нижчі за мінімум пам'яті (див. нижче).
Мінімальний (інструменти для зображень, файлів і легкі PDF-інструменти; без AI)
| Ресурс | Вимога |
|---|---|
| CPU | 2 ядра |
| RAM | 2 GB |
| Диск | ~5.5 GB (образ) + том даних |
| GPU | Не потрібен |
Усі 222 не-AI інструменти каталогу — для зображень (зміна розміру, обрізання, конвертація, стиснення, коригування, водяний знак), відео (обрізання, вимкнення звуку, ремукс), аудіо (конвертація, нормалізація, обрізання), PDF (об'єднання, розділення, стиснення, обертання, захист), конвертації файлів і спеціальні пресети конвертації — працюють на скромному апаратному забезпеченні. Більшість операцій завершуються значно менш ніж за секунду навіть на великому файлі: зображення 2.7 MB змінює розмір за ~0.05 с і переклодовується у WebP за ~2 с.
Мінімум пам'яті реальний, за результатами перебору лімітів ресурсів Docker: 512 MB не можуть запустити стек (навіть одна зміна розміру зображення завершується примусово), 1 GB справляється з однофайловими операціями, але пакет із кількома файлами вичерпує пам'ять, а 2 GB / 2 ядра — це найменша конфігурація, яка комфортно справляється з пакетами.
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 на GPU compose).
Рекомендований (AI-інструменти на CPU)
| Ресурс | Вимога |
|---|---|
| CPU | 4 ядра |
| RAM | 4 GB |
| Disk | 3 ГБ (зображення) + близько 20 ГБ (усі додаткові пакети AI) + робочий простір |
| GPU | Не потрібен (резервний варіант на CPU) |
Встановлення та запуск більших пакетів штучного інтелекту – це те, що підштовхує рекомендацію до 4 ГБ RAM. Без встановлення додаткових пакетів програма простоює близько 360 МБ. Застарілі інструменти Python використовують sidecar, тоді як точний OCR використовує виділений довготривалий dispatcher, закріплений на активному незмінному поколінні. Перед активацією інсталятор запускає smoke test на кандидаті. Потім він автоматично перемикається на новий dispatcher і зливає попередній dispatcher перед garbage collection. Кожен офіційний артефакт точного OCR повинен передати свій найгірший release suite всередині 4 GiB cgroup, тоді як рекомендація хосту 4 ГБ залишає запас для програми Node.js, Postgres, Redis, черг і одночасної роботи.
Більшість AI-інструментів цілком придатні до використання на CPU; парі з них справді потрібен GPU. Виміряно на сучасному 4-ядерному CPU:
| AI-інструмент | Час на CPU | Придатний на CPU? |
|---|---|---|
| Виявлення облич (blur-faces, smart-crop, red-eye), noise-removal | менш ніж 1 с | Так |
| OCR, транскрипція, субтитри | 1-3 с | Так |
| Colorize, покращення облич | ~10 с | Так |
| Видалення / заміна / розмиття фону | ~29 с | Так (доведеться зачекати) |
| AI-збільшення роздільності (RealESRGAN) | ~33 с для малих; хвилини на великих зображеннях | Гранично — GPU наполегливо рекомендується |
| Реставрація фото (повний конвеєр) | кілька хвилин | Ні — потрібен GPU або швидкий багатоядерний CPU |
SnapOtter навмисно не запікає ці завантаження моделей у образ Docker. AI-бандли завантажуються лише тоді, коли адміністратор вмикає відповідний інструмент, зберігаються в постійному томі /data/ai і використовуються спільно кожним інструментом, що залежить від того самого стеку моделей. Це зберігає фінальний образ контейнера малим, водночас дозволяючи повному AI-встановленню досягти більших чисел сховища нижче.
Деякі інструменти залежать від більш ніж одного спільного бандла. Наприклад, Passport Photo потребує і background-removal, і face-detection; якщо background-removal вже встановлено, увімкнення Passport Photo завантажує лише відсутній бандл face-detection. Те саме повторне використання застосовується до всіх AI-інструментів.
Приблизний обсяг пам’яті додаткового набору AI:
| Бандл | Розмір на диску |
|---|---|
| Видалення фону | 4-5 GB |
| Upscale + Покращення облич + Видалення шуму | 5-6 GB |
| Виявлення облич | 200-300 MB |
| Стирання об'єктів + Colorize | 1-2 GB |
Точний OCR (balanced/best) |
~208-234 MiB завантажити / ~409-488 MiB встановити |
| Реставрація фото | 4-5 GB |
| Транскрипція | ~600 МБ |
| Усі набори | ~20 ГБ встановлено |
Швидкий OCR вбудовано в образ за допомогою Tesseract, додає близько 25 MiB і не потребує додаткового пакета OCR або його вимог до пам’яті 4 GiB. Точна упаковка є в офіційному Linux amd64 і arm64 контейнери та прогони ONNX Runtime на CPU. Хости NVIDIA використовують те саме середовище виконання CPU OCR, тому OCR не залежить від версії CUDA або архітектури GPU. Точний час виконання вимагає принаймні 4 GiB ефективної пам’яті: обмеження налаштованого контейнера cgroup, інакше пам’ять хоста. SnapOtter відхиляє системи, нижче встановленого мінімуму сумісності перед завантаженням пакета. Встановлення точного пакета також відхиляється для bare-metal/попередньо зібраних архівів, чиї libc і Python ABI не можуть бути гарантовані.
Репліки, що спільно використовують той самий DATA_DIR, мають працювати на однаковій архітектурі ЦП; закріплюйте розгортання з кількома репліками за сумісними вузлами за допомогою спорідненості вузлів (node affinity). Для змішаних реплік amd64/arm64 потрібні окремі томи даних і незалежні розгортання SnapOtter.
Точний час виконання зберігає одне активне покоління та очищає його кеш завантажень після активації. Для цього випуску перша інсталяція тимчасово потребує приблизно 620-720 MiB для архіву та постановки, а оновлення може досягти піку близько 1,2 GiB, поки старе покоління залишається активним. Програма інсталяції обчислює точні вимоги з підписаного індексу та поточних поколінь перед завантаженням або витягуванням і зазнає помилки раніше, якщо обсяг даних замалий.
deploy:
resources:
limits:
cpus: '4'
memory: 4G
Повний (AI-інструменти на NVIDIA CUDA)
| Ресурс | Вимога |
|---|---|
| CPU | 6-8 ядер (підготовка відео + паралельність працюють на CPU навіть із GPU AI) |
| RAM | 8 GB |
| GPU | NVIDIA з 8+ GB VRAM (12 GB рекомендовано) |
| Диск | ~35 GB загалом |
NVIDIA GPU (CUDA) різко пришвидшує важкі моделі AI. Виміряно на RTX 4070 порівняно із сучасним CPU:
| AI-інструмент | Прискорення з GPU | Примітки |
|---|---|---|
| AI-збільшення роздільності (RealESRGAN 2×) | ~47× | Найбільший виграш — менш ніж секунда проти ~33 с (хвилини на великих зображеннях) |
| Покращення облич (CodeFormer) | ~12× | ~0.9 с проти ~11 с |
| Транскрипція (Whisper) | ~4.5× | |
| Видалення / заміна / розмиття фону | ~4× | ~7 с на GPU проти ~29 с на CPU |
| Colorize | ~1.8× | |
| OCR, виявлення облич, red-eye, noise-removal | ~1× | Уже швидкі на CPU — GPU не допомагає |
| Реставрація фото | немає | Обмежена CPU навіть на GPU (0% використання GPU); тут швидкий CPU важливіший за GPU |
Інструменти, для яких GPU того вартий, — це upscale, покращення облич, транскрипція і видалення фону. Виявлення облич, OCR і red-eye обмежені CPU й уже швидкі, тож GPU нічого не додає.
Пікове використання VRAM сягає 7.5 GB під час upscale з покращенням облич. NVIDIA GPU на 6 GB працює для більшості AI-інструментів окремо, але не впорається з upscale. VRAM 8-12 GB справляється з усім.
Прискорення на iGPU Intel/AMD через VA-API, Quick Sync або OpenCL наразі не підтримується для AI-інференсу. Проброс /dev/dri у контейнер не вмикає прискорення AI на GPU; SnapOtter запускатиме AI-інструменти на CPU, доки не буде доступний NVIDIA CUDA.
deploy:
resources:
limits:
cpus: '4'
memory: 8G
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
Одночасні користувачі
Паралельні запити зміни розміру зображень до стандартного контейнера застосунку, обмеженого 4 ядрами:
| Одночасні запити | Середній час відповіді | Помилки |
|---|---|---|
| 1 | 0.4с | 0 |
| 5 | 1.2с | 0 |
| 10 | 2.1с | 0 |
Час відповіді погіршується сублінійно без помилок у міру насичення пулу воркерів. Підняття ліміту cpus: контейнера застосунку (або використання хоста з більшою кількістю ядер) підіймає стелю. Зверніть увагу, що важкі завдання (транскодування відео, AI на CPU) утримують воркер на весь свій час виконання, тож підбирайте CPU під очікувану кількість одночасних важких завдань, а не лише під кількість запитів.
Підтримувані формати зображень
SnapOtter підтримує 55+ вхідних форматів і 14 вихідних форматів, включно з RAW-файлами від 20+ брендів камер, професійними форматами (PSD, EPS, OpenEXR, HDR), сучасними кодеками (JPEG XL, AVIF, HEIC, QOI) та науковими/ігровими форматами (FITS, DDS).
Див. повний перелік форматів щодо деталей про кожен підтримуваний формат, використовуваний декодер і доступні засоби контролю якості.
Відомі обмеження
- Зміна розміру з урахуванням вмісту аварійно завершується на великих зображеннях (>5 MP) через обмеження в бінарному файлі caire. Добре працює з меншими зображеннями.
- Декодування HEIF займає 13-23 секунди. HEIC (варіант Apple) значно швидший — 0.3-0.9 секунди.
- Upscale дає таймаут на CPU для всього, крім малих зображень. Для практичного використання потрібен GPU.
- CodeFormer для покращення облич значно повільніший за GFPGAN (53с проти 2с на GPU). GFPGAN рекомендовано для більшості сценаріїв.
Томи
| Точка монтування / Том | Призначення | Обов'язковий? |
|---|---|---|
/data (застосунок) |
Моделі AI, Python venv, файли користувачів | Так — без нього втрата файлів |
/tmp/workspace (застосунок) |
Тимчасові файли обробки (автоочищення) | Рекомендовано |
SnapOtter-pgdata (postgres) |
Каталог даних PostgreSQL (користувачі, налаштування, конвеєри, завдання) | Так — без нього втрата даних |
SnapOtter-redisdata (redis) |
Append-only файл Redis для довговічних черг завдань | Рекомендовано |
Bind mounts проти іменованих томів
Іменовані томи (рекомендовано) — Docker керує правами автоматично:
volumes:
- SnapOtter-data:/data
Bind mounts — правами керуєте ви. Встановіть PUID/PGID відповідно до вашого користувача хоста:
volumes:
- ./SnapOtter-data:/data
environment:
- PUID=1000 # Your host UID (run: id -u)
- PGID=1000 # Your host GID (run: id -g)
Права доступу до сховища
SnapOtter записує у два розташування під час виконання: /data (файли користувачів, логи, моделі AI і Python venv) та /tmp/workspace (тимчасовий робочий простір обробки). Обидва мають бути доступними для запису користувачем, від імені якого працює контейнер. Якщо хоч одне ні, контейнер швидко дає збій під час запуску з повідомленням, яке називає каталог, поточний UID/GID і як це виправити — замість того, щоб завантажитися «здоровим» і потім дати збій під час першого завантаження з незрозумілою помилкою.
Те, як обробляються права, залежить від того, як запускається контейнер:
За замовчуванням (стартує від root, скидається до snapotter) — точка входу стартує від root, виправляє власника змонтованих томів, потім скидається до непривілейованого користувача snapotter через gosu. Іменовані томи працюють без жодної конфігурації. Для bind mounts встановіть PUID/PGID на вашого користувача хоста (вище), щоб файли, які він записує, належали вам.
Kubernetes / OpenShift (не-root через runAsUser) — запущений безпосередньо від імені не-root користувача, контейнер не може сам змінити власника томів, тож оркестратор має зробити їх доступними для запису. Встановіть fsGroup:
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(вбудований користувачsnapotterSnapOtter), щоб він відповідав власності образу. -
chownнабір даних хоста на UID, від якого працює контейнер, з оболонки TrueNAS:# Використайте UID з помилки запуску (або запустіть `id` всередині контейнера) chown -R 568:568 /mnt/<pool>/<dataset>
Помилка запуску називає точний UID для використання, тож найшвидший шлях — запустити застосунок один раз, прочитати повідомлення, потім chown (або скоригувати користувача) відповідно.
Змінні середовища
| Змінна | За замовчуванням | Опис |
|---|---|---|
AUTH_ENABLED |
true |
Увімкнути/вимкнути вимогу входу |
DEFAULT_USERNAME |
admin |
Початкове ім'я адміністратора |
DEFAULT_PASSWORD |
admin |
Початковий пароль адміністратора (примусова зміна при першому вході) |
MAX_UPLOAD_SIZE_MB |
0 (необмежено) |
Ліміт завантаження на файл у МБ. Образ постачається зі значенням 0; збірка з вихідного коду стартує зі 100 |
MAX_BATCH_SIZE |
0 (необмежено) |
Максимум файлів на пакетний запит. Образ постачається зі значенням 0; збірка з вихідного коду стартує зі 100 |
RATE_LIMIT_PER_MIN |
1000 |
Запити API за хвилину на IP (встановіть 0, щоб вимкнути) |
MAX_USERS |
0 (необмежено) |
Максимальна кількість облікових записів користувачів |
TRUST_PROXY |
loopback,linklocal,uniquelocal |
Яким вузлам дозволено задавати IP клієнта через X-Forwarded-For. Типово лише приватні мережі |
PUID |
999 |
Запускати з цим UID (для прав bind mount) |
PGID |
999 |
Запускати з цим GID (для прав bind mount) |
LOG_LEVEL |
info |
Рівень деталізації логів: fatal, error, warn, info, debug, trace |
CONCURRENT_JOBS |
0 (авто) |
Максимум паралельних завдань обробки AI |
SESSION_DURATION_HOURS |
168 |
Тривалість сеансу входу (7 днів) |
CORS_ORIGIN |
(порожньо) | Дозволені джерела через кому, або порожньо для того самого джерела |
Вихідний проксі та приватний CA
Офіційний контейнер забезпечує підтримку проксі середовища Node. Якщо SnapOtter має отримати доступ до сховища часу виконання OCR або інших служб HTTPS через корпоративний проксі, установіть HTTPS_PROXY (і HTTP_PROXY, якщо потрібно). Встановіть для NO_PROXY розділений комами список хостів, до яких потрібно отримати прямий доступ, наприклад Postgres, Redis і внутрішнє сховище об’єктів.
Якщо проксі-сервер або внутрішня служба підписана приватним центром сертифікації, підключіть сертифікат ЦС лише для читання та вкажіть на нього NODE_EXTRA_CA_CERTS. Файл повинен існувати на момент запуску процесу Node:
services:
app:
environment:
HTTPS_PROXY: http://proxy.example.internal:3128
HTTP_PROXY: http://proxy.example.internal:3128
NO_PROXY: postgres,redis,minio,localhost,127.0.0.1
NODE_EXTRA_CA_CERTS: /etc/snapotter/custom-ca.pem
volumes:
- ./company-ca.pem:/etc/snapotter/custom-ca.pem:ro
Зберігайте облікові дані проксі за межами файлу Compose (наприклад, у захищеному файлі .env або секреті). Не вимикайте перевірку TLS: підписаний індекс OCR автентифікує метадані випуску, тоді як звичайна перевірка TLS все ще захищає транспорт і всі вихідні запити.
Перевірка стану
Контейнер містить вбудовану перевірку стану:
# 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"}
Зворотний проксі
Типово TRUST_PROXY має значення loopback,linklocal,uniquelocal, тож SnapOtter вірить заголовку X-Forwarded-For лише від вузла з приватної мережі. Зворотному проксі на тому самому хості, у мережі Docker або у вашій локальній мережі довіра є одразу, а отже обмеження швидкості, захист входу від перебору паролів, журнал аудиту та список дозволених IP в enterprise-версії бачать справжній IP клієнта без жодного налаштування.
Задавайте TRUST_PROXY=true лише тоді, коли проксі попереду звертається до SnapOtter з публічної адреси, наприклад хмарний балансувальник навантаження в іншій мережі. На безпосередньо відкритому екземплярі це значення віддає request.ip під контроль зловмисника: той, хто змінює заголовок, отримує свіжий лічильник обмеження швидкості на кожен запит.
Дві речі варто знати, перш ніж вимірювати IP клієнтів. Docker Desktop на macOS і Windows обслуговує опублікований порт через проксі в просторі користувача, який переписує будь-яку адресу джерела на шлюз віртуальної машини 192.168.65.1, тож там жодне значення TRUST_PROXY не поверне справжнього клієнта; усе, що дивиться в інтернет, розгортайте на Linux. І на будь-якій платформі звернення до опублікованого порту через localhost бачиться як шлюз мосту, а не як ваш клієнт, тому перевірка через localhost нічого не каже про те, як визначається справжній клієнт. Повну таблицю значень TRUST_PROXY та застереження щодо Docker Desktop наведено в SECURITY.md.
Дві речі важливі для кожного проксі нижче: дозволити великі тіла запитів (завантаження) і не буферизувати відповіді. Проксі-сервер із буферизацією відповідей порушує прогрес SSE і, що більш помітно, робить завантаження великого файлу «початком, але ніколи не завершенням», оскільки проксі-сервер зберігає весь файл перед його передачею. SnapOtter надсилає X-Accel-Buffering: no під час завантажень, тому 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 (пакети, штучний інтелект, встановлення функцій) і для завантаження великих файлів.
proxy_buffering off;
proxy_read_timeout 300s;
}
}
Nginx Proxy Manager
- Додайте новий Proxy Host
- Встановіть Domain Name на ваш домен
- Встановіть Scheme на
http, Forward Hostname наSnapOtter(або IP вашого контейнера), Forward Port на1349 - Увімкніть підтримку WebSocket
- В Advanced додайте:
client_max_body_size 500M;іproxy_buffering off;
Traefik
# 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
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
cloudflared tunnel --url http://localhost:1349
Примітка: Cloudflare має ліміт завантаження 100 MB на безкоштовних планах. Встановіть MAX_UPLOAD_SIZE_MB=100 відповідно.
CI/CD
Репозиторій GitHub має три робочі процеси:
- ci.yml — Запускається автоматично при кожному push і PR. Виконує лінтинг, перевірку типів, тести, збирання і валідацію образу Docker (без push).
- release.yml — Запускається вручну через
workflow_dispatch. Виконує semantic-release для створення тегу версії й релізу GitHub, потім збирає мультиархітектурний образ Docker (amd64 + arm64) і робить push у Docker Hub (snapotter/snapotter) та GitHub Container Registry (ghcr.io/snapotter-hq/snapotter). - deploy-docs.yml — Збирає цей сайт документації і розгортає його в Cloudflare Pages при push до
main.
Щоб створити реліз, перейдіть до Actions > Release > Run workflow в UI GitHub, або виконайте:
gh workflow run release.yml
Semantic-release визначає версію з історії комітів. Тег Docker latest завжди вказує на останній реліз.
Аналітика
SnapOtter містить анонімну продуктову аналітику (шаблони використання інструментів, звіти про помилки), щоб допомогти виловлювати баги й покращувати функції. Вона увімкнена за замовчуванням. Ваші файли, імена файлів і персональні дані ніколи не є її частиною. SnapOtter працює нормально з вимкненою аналітикою.
Вимкнення аналітики
Відмова під час виконання — це перемикач адміністратора в один клік. Відкрийте Settings > System > Privacy і вимкніть Anonymous Product Analytics. Вона зупиняється негайно для всього екземпляра, без потреби у повторному збиранні.
Для образу, який ніколи не може надсилати аналітику, встановіть жорстке вимкнення на етапі збирання, клонувавши репозиторій і зібравши повторно:
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:
services:
snapotter:
build:
context: .
dockerfile: docker/Dockerfile
args:
SNAPOTTER_ANALYTICS: "off"