Files
SnapOtter/apps/docs/fr/guide/deployment.md
T
SnapOtterandGitHub d10d0f544f fix: release QA hardening across processing, media, security, and CI gates (#649)
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.
2026-07-27 15:37:30 +08:00

36 KiB
Raw Blame History

description, i18n_source_hash, i18n_provenance, i18n_output_hash, i18n_hash_version
description i18n_source_hash i18n_provenance i18n_output_hash i18n_hash_version
Déployez SnapOtter en production avec Docker. Exigences matérielles, configuration GPU et configs de reverse proxy pour Nginx, Traefik et Cloudflare. 2a722f86da75 human 35ed0e32b68f 2

Déploiement

SnapOtter se déploie sous la forme d'une pile Docker Compose à 3 conteneurs : l'image applicative SnapOtter, PostgreSQL 17 et Redis 8. L'image applicative prend en charge linux/amd64 (avec NVIDIA CUDA pour l'accélération de l'IA) et linux/arm64 (CPU), elle s'exécute donc nativement sur les serveurs Intel/AMD, les Mac Apple Silicon et les appareils ARM comme le Raspberry Pi 4/5. L'accélération par iGPU Intel/AMD via VA-API, Quick Sync ou OpenCL n'est pas prise en charge pour l'inférence IA aujourd'hui.

Consultez Image Docker pour la configuration GPU, les exemples Docker Compose et l'épinglage de version.

::: info Compatibilité de lOCR coréen LOCR rapide prend en charge auto, en, de, es, fr, zh et ja, mais pas le coréen (ko). Le coréen nécessite le pack OCR précis et balanced ou best. Le pack fonctionne dans les conteneurs Linux amd64 et arm64 officiels, y compris sur les hôtes NVIDIA où lOCR reste exécuté sur le CPU. Un système non pris en charge reçoit une erreur de compatibilité explicite, sans repli silencieux vers fast. Le coréen avec fast ou lalias historique tesseract est refusé avant la mise en file avec FEATURE_INCOMPATIBLE et fast-korean-unsupported. :::

Démarrage rapide (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

L'application est ensuite disponible sur http://localhost:1349.

Limites de débit Docker Hub ? Remplacez snapotter/snapotter:latest par ghcr.io/snapotter-hq/snapotter:latest pour récupérer l'image depuis GitHub Container Registry à la place. Les deux registres reçoivent la même image à chaque publication.

Démarrage rapide (NVIDIA CUDA)

Pour laccélération NVIDIA CUDA sur les outils dIA pris en charge (suppression de larrière-plan, mise à l’échelle, amélioration du visage) :

# 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     # Changez ceci pour les déploiements non locaux
      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

Vérifier l'accélération GPU

Vérifiez la détection CUDA dans les journaux :

docker logs SnapOtter 2>&1 | head -20
# Look for: [gpu] CUDA available via torch

Si les outils d'IA s'exécutent sur le processeur même si --gpus all et NVIDIA Container Toolkit sont correctement configurés, réinstallez le bundle concerné (par exemple Suppression de l'arrière-plan) depuis Paramètres → Fonctionnalités IA. Le programme d'installation restaure la version GPU d'ONNX Runtime, qu'une version CPU uniquement extraite par un autre ensemble (tel que la transcription) peut autrement masquer dans l'environnement d'IA partagé. Si la réinstallation à partir de l'interface utilisateur ne restaure pas le GPU sur une image plus ancienne, consultez la réparation manuelle dans [numéro 490] (https://github.com/snapotter-hq/SnapOtter/issues/490).

Exigences matérielles

Ces chiffres proviennent de tests de performance sur toute une gamme de systèmes, d'un poste de travail amd64 moderne équipé d'une NVIDIA RTX 4070 jusqu'à un Raspberry Pi, en exécutant l'intégralité du catalogue d'outils sur chacun et en balayant les limites de ressources Docker pour trouver le plancher réel.

Vous tournez au bas de ces niveaux (un Pi, un vieux portable, un VPS de 2 Go) ? Configurations à ressources limitées transforme ces chiffres en un pas à pas concret avec des plafonds ajustés.

Référence rapide

Niveau Cas d'usage CPU RAM GPU Stockage
Minimum Outils image, fichiers et PDF légers ; utilisateur unique ; petits lots 2 cœurs 2 Go Aucun ~7 Go
Recommandé Les cinq modalités, y compris vidéo, PDF et IA sur CPU ; lots ; quelques utilisateurs 4 cœurs 4 Go Aucun ~25 Go
Complet Tout à pleine vitesse, y compris IA sur GPU ; grands lots ; nombreux utilisateurs 6-8 cœurs 8 Go NVIDIA 8 Go+ de VRAM (12 Go confortable) ~35 Go

Architecture : 64 bits uniquement (linux/amd64 ou linux/arm64). SnapOtter s'exécute nativement sur les serveurs Intel/AMD, les Mac Apple Silicon et les cartes ARM 64 bits, y compris le Raspberry Pi 4 et 5 (4-8 Go). Il ne fonctionne pas sur ARM 32 bits (armv7/armhf), aucune image n'étant construite pour cette cible, ni sur les cartes de la classe 512 Mo comme le Pi Zero, qui sont sous le plancher mémoire (voir ci-dessous).

Minimum (outils image, fichiers et PDF légers ; sans IA)

Ressource Exigence
CPU 2 cœurs
RAM 2 Go
Disque ~5,5 Go (image) + volume de données
GPU Non requis

Les 222 outils non-IA du catalogue - image (redimensionner, rogner, convertir, compresser, ajuster, filigraner), vidéo (couper, rendre muet, remultiplexer), audio (convertir, normaliser, couper), PDF (fusionner, diviser, compresser, pivoter, protéger), conversions de fichiers et préréglages de conversion dédiés - s'exécutent sur du matériel modeste. La plupart des opérations se terminent en bien moins d'une seconde, même sur un gros fichier : une image de 2,7 Mo est redimensionnée en ~0,05 s et réencodée en WebP en ~2 s.

Le plancher mémoire est réel, d'après un balayage des limites de ressources Docker : 512 Mo ne peuvent pas démarrer la pile (même un simple redimensionnement d'image est tué), 1 Go gère les opérations sur un seul fichier mais un lot multi-fichiers manque de mémoire, et 2 Go / 2 cœurs est la plus petite configuration qui gère les lots confortablement.

deploy:
  resources:
    limits:
      cpus: '2'
      memory: 2G

La seule exception gourmande en CPU est le réencodage vidéo. Les opérations de copie de flux (couper, rendre muet, remultiplexage de conteneur) sont instantanées, mais le transcodage vers un codec différent est limité par le CPU. Un clip 1080p / 45 secondes réencodé en VP9 (WebM) prend environ ~40 s sur un CPU moderne rapide, ~45 s sur Apple Silicon, ~80 s sur un ancien 4 cœurs mobile et ~130 s sur un ancien serveur 4 cœurs. Si votre charge de travail est axée sur la vidéo, privilégiez les cœurs CPU et la fréquence d'horloge, ou augmentez la limite cpus: du conteneur : le compose fourni plafonne l'application à 4 cœurs par défaut (8 sur le compose GPU).

Ressource Exigence
CPU 4 cœurs
RAM 4 Go
Disk 3 Go (image) + environ 20 Go (tous les packs AI en option) + espace de travail
GPU Non requis (repli sur CPU)

L'installation et l'exécution des plus gros bundles d'IA sont ce qui pousse la recommandation à 4 Go de RAM. Sans packs optionnels installés, l'application reste inactive autour de 360 Mo. Les anciens outils Python partagent un sidecar, tandis que les outils OCR précis utilisent un dispatcher dédié à longue durée de vie épinglé à la génération active immuable. Avant l'activation, le programme d'installation exécute un smoke test sur le candidat. Il passe ensuite de manière atomique au nouveau dispatcher et draine le dispatcher précédent avant garbage collection. Chaque artefact OCR précis officiel doit transmettre son release suite le plus défavorable dans un 4 GiB cgroup, tandis que la recommandation d'hôte de 4 Go laisse une marge pour l'application Node.js, Postgres, Redis, les files d'attente et le travail simultané.

La plupart des outils IA sont parfaitement utilisables sur CPU ; deux ou trois veulent vraiment un GPU. Mesuré sur un CPU 4 cœurs moderne :

Outil IA Temps CPU Utilisable sur CPU ?
Détection de visages (flouter les visages, recadrage intelligent, yeux rouges), suppression du bruit moins de 1 s Oui
OCR, transcription, sous-titres 1-3 s Oui
Coloriser, amélioration des visages ~10 s Oui
Suppression / remplacement / floutage d'arrière-plan ~29 s Oui (il faudra patienter)
Agrandissement IA (RealESRGAN) ~33 s sur petit format ; plusieurs minutes sur les grandes images Limite - GPU fortement recommandé
Restauration de photo (pipeline complet) plusieurs minutes Non - nécessite un GPU ou un CPU rapide à nombreux cœurs

SnapOtter n'intègre volontairement pas ces téléchargements de modèles dans l'image Docker. Les bundles IA ne sont récupérés que lorsqu'un administrateur active l'outil concerné, stockés dans le volume persistant /data/ai et partagés par chaque outil qui dépend de la même pile de modèles. Cela maintient l'image finale du conteneur petite tout en permettant à une installation IA complète d'atteindre les chiffres de stockage plus élevés ci-dessous.

Certains outils dépendent de plus d'un bundle partagé. Par exemple, Photo d'identité a besoin à la fois de background-removal et de face-detection ; si background-removal est déjà installé, activer Photo d'identité ne télécharge que le bundle face-detection manquant. La même réutilisation s'applique à tous les outils IA.

Estimations de stockage du pack AI en option :

Bundle Taille disque
Suppression d'arrière-plan 4-5 Go
Agrandissement + Amélioration des visages + Suppression du bruit 5-6 Go
Détection de visages 200-300 Mo
Gomme d'objets + Coloriser 1-2 Go
OCR précis (balanced/best) ~208-234 MiB téléchargé / ~409-488 MiB installé
Restauration de photo 4-5 Go
Transcription ~600 Mo
Tous les forfaits ~20 Go installés

Fast OCR est intégré à l'image via Tesseract, ajoute environ 25 MiB et ne nécessite pas le pack OCR en option ni ses 4 exigences de mémoire GiB. Le pack précis est disponible dans les conteneurs officiels Linux amd64 et arm64 et exécute ONNX Runtime sur CPU. Les hôtes NVIDIA utilisent le même environnement d'exécution CPU OCR, donc OCR ne dépend pas de la version CUDA ou de l'architecture GPU. Le temps d'exécution précis nécessite au moins 4 GiB de mémoire effective : la limite cgroup du conteneur configuré, sinon la mémoire hôte. SnapOtter rejette les systèmes inférieurs au minimum de compatibilité signé avant de télécharger le pack. L'installation d'un pack précis est également rejetée sur les archives bare-metal/préconstruites dont les libc et Python ABI ne peuvent pas être garanties.

Les répliques qui partagent le même DATA_DIR doivent utiliser la même architecture de processeur ; épinglez les déploiements à plusieurs répliques à des nœuds compatibles au moyen de l'affinité de nœuds. Les répliques amd64/arm64 mixtes nécessitent des volumes de données distincts et des déploiements SnapOtter indépendants.

Le runtime précis conserve une génération active et purge son cache de téléchargement après l'activation. Pour cette version, une première installation nécessite temporairement environ 620-720 MiB pour l'archive plus le staging, et une mise à niveau peut culminer près de 1,2 GiB tandis que l'ancienne génération reste active. Le programme d'installation calcule les exigences exactes à partir de l'index signé et des générations actuelles avant le téléchargement ou l'extraction, et échoue prématurément si le volume de données est trop petit.

deploy:
  resources:
    limits:
      cpus: '4'
      memory: 4G

Complet (outils IA sur NVIDIA CUDA)

Ressource Exigence
CPU 6-8 cœurs (la préparation vidéo + la concurrence s'exécutent sur CPU même avec l'IA sur GPU)
RAM 8 Go
GPU NVIDIA avec 8+ Go de VRAM (12 Go recommandé)
Disque ~35 Go au total

Un GPU NVIDIA (CUDA) accélère considérablement les modèles IA lourds. Mesuré sur une RTX 4070 par rapport à un CPU moderne :

Outil IA Accélération avec GPU Notes
Agrandissement IA (RealESRGAN 2×) ~47× Le plus gros gain - moins d'une seconde contre ~33 s (plusieurs minutes sur les grandes images)
Amélioration des visages (CodeFormer) ~12× ~0,9 s contre ~11 s
Transcription (Whisper) ~4,5×
Suppression / remplacement / floutage d'arrière-plan ~4× ~7 s sur GPU contre ~29 s sur CPU
Coloriser ~1,8×
OCR, détection de visages, yeux rouges, suppression du bruit ~1× Déjà rapide sur CPU - un GPU n'apporte rien
Restauration de photo aucune Limité par le CPU même sur un GPU (0 % d'utilisation du GPU) ; un CPU rapide compte plus qu'un GPU ici

Les outils qui valent un GPU sont l'agrandissement, l'amélioration des visages, la transcription et la suppression d'arrière-plan. La détection de visages, l'OCR et les yeux rouges sont limités par le CPU et déjà rapides, un GPU n'apporte donc rien.

L'utilisation de VRAM en pic atteint 7,5 Go pendant un agrandissement avec amélioration des visages. Un GPU NVIDIA de 6 Go convient pour la plupart des outils IA pris individuellement, mais échouera sur l'agrandissement. 8-12 Go de VRAM gèrent tout.

L'accélération par iGPU Intel/AMD via VA-API, Quick Sync ou OpenCL n'est pas prise en charge pour l'inférence IA aujourd'hui. Mapper /dev/dri dans le conteneur n'active pas l'accélération GPU de l'IA ; SnapOtter exécutera les outils IA sur CPU sauf si NVIDIA CUDA est disponible.

deploy:
  resources:
    limits:
      cpus: '4'
      memory: 8G
    reservations:
      devices:
        - driver: nvidia
          count: all
          capabilities: [gpu]

Utilisateurs simultanés

Requêtes de redimensionnement d'image parallèles sur le conteneur applicatif plafonné à 4 cœurs par défaut :

Requêtes simultanées Temps de réponse moyen Erreurs
1 0,4 s 0
5 1,2 s 0
10 2,1 s 0

Le temps de réponse se dégrade de manière sous-linéaire sans erreur à mesure que le pool de workers sature. Augmenter la limite cpus: du conteneur applicatif (ou utiliser un hôte avec plus de cœurs) relève le plafond. Notez que les tâches lourdes (transcodage vidéo, IA sur CPU) mobilisent un worker pendant toute leur durée, dimensionnez donc le CPU selon votre nombre attendu de tâches lourdes simultanées, et pas seulement selon le nombre de requêtes.

Formats d'image pris en charge

SnapOtter prend en charge 55+ formats d'entrée et 14 formats de sortie, dont les fichiers RAW de 20+ marques d'appareils photo, les formats professionnels (PSD, EPS, OpenEXR, HDR), les codecs modernes (JPEG XL, AVIF, HEIC, QOI) et les formats scientifiques/de jeu (FITS, DDS).

Consultez la liste complète des formats pour les détails sur chaque format pris en charge, le décodeur utilisé et les contrôles de qualité disponibles.

Limitations connues

  • Le redimensionnement sensible au contenu plante sur les grandes images (>5 MP) en raison d'une limitation du binaire caire. Fonctionne bien avec des images plus petites.
  • Le décodage HEIF prend 13-23 secondes. HEIC (la variante d'Apple) est bien plus rapide, à 0,3-0,9 seconde.
  • L'agrandissement expire sur CPU pour tout ce qui dépasse les petites images. GPU requis pour un usage pratique.
  • L'amélioration des visages CodeFormer est nettement plus lente que GFPGAN (53 s contre 2 s sur GPU). GFPGAN est recommandé pour la plupart des cas d'usage.

Volumes

Montage / Volume Rôle Requis ?
/data (app) Modèles IA, venv Python, fichiers utilisateur Oui - perte de fichiers sans lui
/tmp/workspace (app) Fichiers de traitement temporaires (nettoyés automatiquement) Recommandé
SnapOtter-pgdata (postgres) Répertoire de données PostgreSQL (utilisateurs, paramètres, pipelines, tâches) Oui - perte de données sans lui
SnapOtter-redisdata (redis) Fichier append-only Redis pour des files de tâches durables Recommandé

Montages liés (bind mounts) vs volumes nommés

Volumes nommés (recommandés) - Docker gère les permissions automatiquement :

volumes:
  - SnapOtter-data:/data

Montages liés - Vous gérez les permissions. Réglez PUID/PGID pour correspondre à votre utilisateur hôte :

volumes:
  - ./SnapOtter-data:/data
environment:
  - PUID=1000    # Your host UID (run: id -u)
  - PGID=1000    # Your host GID (run: id -g)

Permissions de stockage

SnapOtter écrit à deux emplacements à l'exécution : /data (fichiers utilisateur, journaux, modèles IA et le venv Python) et /tmp/workspace (espace de travail temporaire de traitement). Les deux doivent être accessibles en écriture par l'utilisateur sous lequel le conteneur s'exécute. Si l'un ne l'est pas, le conteneur échoue rapidement au démarrage avec un message nommant le répertoire, l'UID/GID en cours d'exécution et comment corriger, au lieu de démarrer « en bonne santé » puis d'échouer au premier téléversement avec une erreur cryptique.

La façon dont les permissions sont gérées dépend de la manière dont le conteneur est lancé :

Par défaut (démarre en root, redescend vers snapotter) - le point d'entrée démarre en root, corrige la propriété des volumes montés, puis redescend vers l'utilisateur non privilégié snapotter via gosu. Les volumes nommés fonctionnent sans aucune configuration. Pour les montages liés, réglez PUID/PGID sur votre utilisateur hôte (ci-dessus) afin que les fichiers qu'il écrit vous appartiennent.

Kubernetes / OpenShift (non-root via runAsUser) - lancé directement en tant qu'utilisateur non-root, le conteneur ne peut pas chown les volumes lui-même, l'orchestrateur doit donc les rendre accessibles en écriture. Réglez fsGroup :

securityContext:
  runAsUser: 999
  runAsGroup: 999
  fsGroup: 999        # makes mounted volumes writable by the pod

Les répertoires accessibles en écriture de l'image appartiennent au groupe GID 0 et sont accessibles en écriture par le groupe, de sorte qu'un pod s'exécutant avec un UID arbitraire plus le groupe supplémentaire root (le défaut OpenShift) peut écrire sans chown.

TrueNAS Scale (et autres configurations à « UID étranger ») - TrueNAS exécute les applications sous un utilisateur non-root (souvent 568:568) et monte des jeux de données hôtes appartenant à un autre utilisateur, de sorte que ni le point d'entrée ni fsGroup ne les rendent accessibles en écriture par lui-même. Choisissez l'une des options :

  • Exécuter l'application en root (recommandé) - laissez l'utilisateur de l'application non défini ou réglez-le sur 0, et laissez le point d'entrée par défaut corriger les permissions et redescendre vers snapotter.

  • Exécuter en tant qu'UID 999 - réglez l'utilisateur/groupe de l'application sur 999:999 (l'utilisateur intégré snapotter de SnapOtter) pour qu'il corresponde à la propriété de l'image.

  • chown le jeu de données hôte vers l'UID sous lequel le conteneur s'exécute, depuis le shell TrueNAS :

    # Utilisez l'UID de l'erreur de démarrage (ou exécutez `id` dans le conteneur)
    chown -R 568:568 /mnt/<pool>/<dataset>
    

L'erreur de démarrage nomme l'UID exact à utiliser, le chemin le plus rapide est donc de démarrer l'application une fois, de lire le message, puis d'exécuter chown (ou d'ajuster l'utilisateur) en conséquence.

Variables d'environnement

Variable Défaut Description
AUTH_ENABLED true Activer/désactiver l'exigence de connexion
DEFAULT_USERNAME admin Nom d'utilisateur admin initial
DEFAULT_PASSWORD admin Mot de passe admin initial (changement forcé à la première connexion)
MAX_UPLOAD_SIZE_MB 0 (illimité) Limite de téléversement par fichier en Mo. L'image est livrée avec 0 ; une compilation depuis les sources démarre à 100
MAX_BATCH_SIZE 0 (illimité) Nombre max de fichiers par requête de lot. L'image est livrée avec 0 ; une compilation depuis les sources démarre à 100
RATE_LIMIT_PER_MIN 1000 Requêtes API par minute et par IP (mettez 0 pour désactiver)
MAX_USERS 0 (illimité) Nombre maximal de comptes utilisateur
TRUST_PROXY loopback,linklocal,uniquelocal Quels pairs peuvent définir l'IP du client via X-Forwarded-For. Réseaux privés uniquement par défaut
PUID 999 Exécuter sous cet UID (pour les permissions de montage lié)
PGID 999 Exécuter sous ce GID (pour les permissions de montage lié)
LOG_LEVEL info Verbosité des journaux : fatal, error, warn, info, debug, trace
CONCURRENT_JOBS 0 (auto) Nombre max de tâches de traitement IA en parallèle
SESSION_DURATION_HOURS 168 Durée de vie de la session de connexion (7 jours)
CORS_ORIGIN (vide) Origines autorisées séparées par des virgules, ou vide pour même origine

Proxy sortant et CA privée

Le conteneur officiel permet la prise en charge du proxy d'environnement de Node. Si SnapOtter doit atteindre le référentiel d'exécution OCR ou d'autres services HTTPS via un proxy d'entreprise, définissez HTTPS_PROXY (et HTTP_PROXY si nécessaire). Définissez NO_PROXY sur une liste d'hôtes séparés par des virgules qui doivent être atteints directement, tels que Postgres, Redis et le stockage d'objets interne.

Si le proxy ou un service interne est signé par une autorité de certification privée, montez le certificat CA en lecture seule et pointez NODE_EXTRA_CA_CERTS vers celui-ci. Le fichier doit exister au démarrage du processus 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

Conservez les informations d'identification du proxy en dehors du fichier Compose (par exemple dans un fichier .env protégé ou secret). Ne désactivez pas la vérification TLS : l'index OCR signé authentifie les métadonnées de version, tandis que la validation TLS normale protège toujours le transport et toutes les autres requêtes sortantes.

Vérification d'état (health check)

Le conteneur inclut une vérification d'état intégrée :

# 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

TRUST_PROXY vaut loopback,linklocal,uniquelocal par défaut : SnapOtter ne croit donc l'en-tête X-Forwarded-For que s'il vient d'un pair situé sur un réseau privé. Un reverse proxy sur le même hôte, sur un réseau Docker ou sur votre LAN est digne de confiance d'emblée, ce qui fait que la limitation de débit, le limiteur de force brute à la connexion, le journal d'audit et la liste d'IP autorisées de l'édition enterprise voient tous l'IP client réelle sans aucune configuration.

Ne mettez TRUST_PROXY=true que lorsque le proxy placé devant atteint SnapOtter depuis une adresse publique, un répartiteur de charge cloud sur un autre réseau par exemple. Sur une instance directement exposée, cette valeur rend request.ip contrôlable par un attaquant, car un appelant qui fait tourner l'en-tête obtient un nouveau compteur de limitation de débit à chaque requête.

Deux choses à savoir avant de vous lancer dans la mesure des IP clientes. Docker Desktop sur macOS et Windows sert un port publié via un proxy en espace utilisateur qui réécrit toutes les adresses source vers la passerelle de la VM 192.168.65.1 ; aucune valeur de TRUST_PROXY n'y récupère le client réel, déployez donc sous Linux tout ce qui est exposé à internet. Et sur n'importe quelle plateforme, atteindre un port publié via localhost est observé comme la passerelle du pont plutôt que comme votre client : un test en localhost ne vous apprend donc rien sur la façon dont un vrai client est attribué. Le tableau complet des valeurs de TRUST_PROXY et la mise en garde sur Docker Desktop se trouvent dans SECURITY.md.

Deux choses comptent pour chaque proxy ci-dessous : autoriser les corps de requêtes volumineux (téléchargements) et ne pas mettre les réponses en mémoire tampon. Un proxy tamponnant les réponses interrompt la progression de SSE et, plus visiblement, fait "démarrer mais ne jamais terminer" le téléchargement d'un fichier volumineux, car le proxy conserve l'intégralité du fichier avant de le transmettre. SnapOtter envoie X-Accel-Buffering: no lors des téléchargements afin que nginx les diffuse même si la mise en mémoire tampon est laissée ailleurs, mais les proxys autres que nginx doivent désactiver explicitement la mise en mémoire tampon des réponses (indiquée dans chaque configuration ci-dessous). Si un téléchargement s'arrête en cours de route, un proxy de mise en mémoire tampon devant est la première chose à vérifier.

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;

        # Diffusez les réponses au lieu de la mise en mémoire tampon : nécessaire pour la progression de SSE (par lots, IA, installations de fonctionnalités) et pour les téléchargements de fichiers volumineux.
        proxy_buffering off;
        proxy_read_timeout 300s;
    }
}

Nginx Proxy Manager

  1. Ajoutez un nouveau Proxy Host
  2. Réglez Domain Name sur votre domaine
  3. Réglez Scheme sur http, Forward Hostname sur SnapOtter (ou l'IP de votre conteneur), Forward Port sur 1349
  4. Activez la prise en charge WebSocket
  5. Sous Advanced, ajoutez : client_max_body_size 500M; et 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 désactive la mise en mémoire tampon des réponses, qui est requise pour les événements de progression SSE (traitement par lots, outils d'IA, installations de fonctionnalités) et pour que les téléchargements de fichiers volumineux soient diffusés au lieu de se bloquer. Les délais d'attente prolongés permettent de télécharger des fichiers volumineux sans que Caddy ne ferme la connexion prématurément.

Cloudflare Tunnels

cloudflared tunnel --url http://localhost:1349

Remarque : Cloudflare impose une limite de téléversement de 100 Mo sur les offres gratuites. Réglez MAX_UPLOAD_SIZE_MB=100 en conséquence.

CI/CD

Le dépôt GitHub comporte trois workflows :

  • ci.yml - S'exécute automatiquement à chaque push et PR. Effectue le lint, la vérification de types, les tests, la construction et la validation de l'image Docker (sans push).
  • release.yml - Déclenché manuellement via workflow_dispatch. Exécute semantic-release pour créer un tag de version et une release GitHub, puis construit une image Docker multi-architecture (amd64 + arm64) et la pousse vers Docker Hub (snapotter/snapotter) et GitHub Container Registry (ghcr.io/snapotter-hq/snapotter).
  • deploy-docs.yml - Construit ce site de documentation et le déploie sur Cloudflare Pages lors d'un push vers main.

Pour créer une release, allez dans Actions > Release > Run workflow dans l'interface GitHub, ou exécutez :

gh workflow run release.yml

Semantic-release détermine la version à partir de l'historique des commits. Le tag Docker latest pointe toujours vers la release la plus récente.

Analytique

SnapOtter inclut une analytique produit anonyme (schémas d'utilisation des outils, rapports d'erreurs) pour aider à détecter les bugs et améliorer les fonctionnalités. Elle est activée par défaut. Vos fichiers, leurs noms et vos données personnelles n'en font jamais partie. SnapOtter fonctionne normalement avec l'analytique désactivée.

Désactiver l'analytique

Le retrait à l'exécution est un basculement admin en un clic. Ouvrez Settings > System > Privacy et désactivez Anonymous Product Analytics. Cela s'arrête immédiatement pour toute l'instance, sans reconstruction requise.

Pour une image qui ne peut jamais émettre d'analytique, définissez l'arrêt matériel au moment de la construction en clonant le dépôt et en le reconstruisant :

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

Ou ajoutez l'argument de construction à votre docker-compose.yml existant :

services:
  snapotter:
    build:
      context: .
      dockerfile: docker/Dockerfile
      args:
        SNAPOTTER_ANALYTICS: "off"