Skip to content

Configuration

All configuration is done through environment variables. Every variable has a sensible default, so SnapOtter works out of the box without setting any of them.

Environment variables

Server

VariableDefaultDescription
PORT1349Port the server listens on.
RATE_LIMIT_PER_MIN1000Maximum requests per minute per IP. Set to 0 to disable rate limiting.
CORS_ORIGIN(empty)Comma-separated allowed origins for CORS, or empty for same-origin only.
LOG_LEVELinfoLog verbosity. One of: fatal, error, warn, info, debug, trace.
TRUST_PROXYtrueTrust X-Forwarded-For headers from a reverse proxy. Set to false if not behind a proxy.

Authentication

VariableDefaultDescription
AUTH_ENABLEDfalseSet to true to require login. The Docker image defaults to true.
DEFAULT_USERNAMEadminUsername for the initial admin account. Only used on first run.
DEFAULT_PASSWORDadminPassword for the initial admin account. Change this after first login.
MAX_USERS0 (unlimited)Maximum number of registered user accounts. Set to 0 for unlimited.
SESSION_DURATION_HOURS168Login session lifetime in hours (default is 7 days).
SKIP_MUST_CHANGE_PASSWORD-Set to any non-empty value to bypass the forced password-change prompt on first login

Storage

VariableDefaultDescription
STORAGE_MODElocallocal or s3. S3/MinIO requires a license with the s3_storage feature.
DATABASE_URLpostgres://snapotter:snapotter@postgres:5432/snapotterPostgreSQL connection string.
REDIS_URLredis://redis:6379Redis connection string (used for BullMQ job queues).
WORKSPACE_PATH./tmp/workspaceDirectory for temporary files during processing. Cleaned up automatically.
FILES_STORAGE_PATH./data/filesDirectory for persistent user files (uploaded images, saved results).

Embedded mode

Run the image with no DATABASE_URL and no REDIS_URL and it starts its own PostgreSQL 17 and Redis inside the container, bound to loopback, with all data on the /data volume. This restores the single-command docker run experience for quick start, homelab, and upgrades from 1.x. It is a convenience path, not a production deployment: for production, run the 3-container Compose stack with separate PostgreSQL and Redis. Embedded mode requires running the container as root and is incompatible with arbitrary-UID runtimes (OpenShift, Kubernetes runAsNonRoot); use Compose there.

VariableDefaultDescription
EMBEDDEDautoAuto-enabled when both DATABASE_URL and REDIS_URL are unset. Set to 0 to disable it (the app then fails fast if no external DATABASE_URL/REDIS_URL is set, rather than silently starting an in-container database).
REDIS_MAXMEMORY512mbMemory cap for the embedded Redis (embedded mode only). Lower it on memory-constrained hosts such as a Raspberry Pi.

Upgrading from 1.x: put your old snapotter.db at /data/snapotter.db in the volume and embedded mode imports it into the embedded PostgreSQL on first boot. The import runs once; later boots skip it.

Telemetry note: embedded mode inherits the image's analytics default like any other configuration. The published image ships with analytics on; build with --build-arg SNAPOTTER_ANALYTICS=off, or use the in-app admin opt-out, to disable it.

Processing limits

VariableDefaultDescription
MAX_UPLOAD_SIZE_MB100Maximum file size per upload in megabytes. Set to 0 for unlimited.
MAX_BATCH_SIZE100Maximum number of files in a single batch request. Set to 0 for unlimited.
CONCURRENT_JOBS0 (auto)Number of batch jobs that run in parallel. Set to 0 to auto-detect based on available CPU cores.
MAX_MEGAPIXELS0 (unlimited)Maximum image resolution allowed in megapixels. Set to 0 for unlimited.
MAX_WORKER_THREADS0 (auto)Maximum worker threads for image processing. Set to 0 to auto-detect based on available CPU cores.
PROCESSING_TIMEOUT_S0 (no limit)Maximum processing time per request in seconds. Set to 0 for no timeout.
MAX_PIPELINE_STEPS20Maximum number of steps in a pipeline. Set to 0 for no limit.
MAX_CANVAS_PIXELS0 (no limit)Maximum canvas size in pixels for output images. Set to 0 for no limit.
MAX_SVG_SIZE_MB0 (unlimited)Maximum SVG file size in megabytes. Set to 0 for unlimited.
MAX_SPLIT_GRID100Maximum grid dimension for the image split tool.
MAX_PDF_PAGES0 (unlimited)Maximum number of PDF pages for PDF-to-image conversion. Set to 0 for unlimited.

Cleanup

VariableDefaultDescription
FILE_MAX_AGE_HOURS72How long unsaved processing results (raw uploads and tool outputs) are kept before automatic deletion. Files you explicitly save to the Files library are not affected and persist until you delete them.
CLEANUP_INTERVAL_MINUTES60How often the cleanup job runs.

Appearance

VariableDefaultDescription
DEFAULT_THEMElightDefault theme for new sessions. light or dark.
DEFAULT_LOCALEenDefault interface language.
DEFAULT_TOOL_VIEWsidebarDefault tool layout. sidebar or fullscreen.

Docker permissions

VariableDefaultDescription
PUID999Run the container process as this UID. Set to match your host user for bind mounts (id -u).
PGID999Run the container process as this GID. Set to match your host group for bind mounts (id -g).

Docker example

yaml
services:
  SnapOtter:
    image: snapotter/snapotter:latest
    ports:
      - "1349:1349"
    volumes:
      - SnapOtter-data:/data
      - SnapOtter-workspace:/tmp/workspace
    environment:
      - AUTH_ENABLED=true
      - DEFAULT_USERNAME=admin
      - DEFAULT_PASSWORD=changeme
      - DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter
      - REDIS_URL=redis://redis:6379
      - MAX_UPLOAD_SIZE_MB=200
      - CONCURRENT_JOBS=4
      - FILE_MAX_AGE_HOURS=12
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped

  postgres:
    image: postgres:17-alpine
    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

  redis:
    image: redis:8-alpine
    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

volumes:
  SnapOtter-data:
  SnapOtter-workspace:
  SnapOtter-pgdata:
  SnapOtter-redisdata:

Volumes

The Docker Compose stack uses four volumes:

  • /data (app) - AI models, Python venv, and user files. Mount this to keep uploaded files and installed AI bundles across restarts.
  • /tmp/workspace (app) - Temporary storage for files being processed. This can be ephemeral, but mounting it avoids filling up the container's writable layer.
  • SnapOtter-pgdata (postgres) - PostgreSQL data directory. This holds all relational data (users, settings, pipelines, jobs, audit log). Back up via pg_dump or volume snapshot.
  • SnapOtter-redisdata (redis) - Redis append-only file for durable job queues.